Atlas - SDL_render_d3d.c
Home / ext / SDL / src / render / direct3d Lines: 1 | Size: 74237 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_RENDER_D3D 24 25#include "../../core/windows/SDL_windows.h" 26 27#include "../SDL_sysrender.h" 28#include "../SDL_d3dmath.h" 29#include "../../video/windows/SDL_windowsvideo.h" 30#include "../../video/SDL_pixels_c.h" 31 32#define D3D_DEBUG_INFO 33#include <d3d9.h> 34 35#include "SDL_shaders_d3d.h" 36 37typedef struct 38{ 39 SDL_Rect viewport; 40 bool viewport_dirty; 41 SDL_Texture *texture; 42 SDL_BlendMode blend; 43 bool cliprect_enabled; 44 bool cliprect_enabled_dirty; 45 SDL_Rect cliprect; 46 bool cliprect_dirty; 47 D3D9_Shader shader; 48 const float *shader_params; 49 bool texture_state_dirty; 50} D3D_DrawStateCache; 51 52typedef struct 53{ 54 bool dirty; 55 int w, h; 56 DWORD usage; 57 Uint32 format; 58 D3DFORMAT d3dfmt; 59 IDirect3DTexture9 *texture; 60 IDirect3DTexture9 *staging; 61} D3D_TextureRep; 62 63struct D3D_PaletteData 64{ 65 D3D_TextureRep texture; 66 67 struct D3D_PaletteData *prev; 68 struct D3D_PaletteData *next; 69}; 70typedef struct D3D_PaletteData D3D_PaletteData; 71 72// Direct3D renderer implementation 73 74typedef struct 75{ 76 void *d3dDLL; 77 IDirect3D9 *d3d; 78 IDirect3DDevice9 *device; 79 UINT adapter; 80 D3DPRESENT_PARAMETERS pparams; 81 bool updateSize; 82 bool beginScene; 83 bool enableSeparateAlphaBlend; 84 SDL_ScaleMode scaleMode[3]; 85 SDL_TextureAddressMode addressModeU[3]; 86 SDL_TextureAddressMode addressModeV[3]; 87 IDirect3DSurface9 *defaultRenderTarget; 88 IDirect3DSurface9 *currentRenderTarget; 89 void *d3dxDLL; 90 LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS]; 91 LPDIRECT3DVERTEXBUFFER9 vertexBuffers[8]; 92 size_t vertexBufferSize[8]; 93 int currentVertexBuffer; 94 bool reportedVboProblem; 95 D3D_DrawStateCache drawstate; 96 D3D_PaletteData *palettes; 97} D3D_RenderData; 98 99typedef struct 100{ 101 D3D_TextureRep texture; 102 UINT shader_params_length; 103 const float *shader_params; 104 float palette_shader_params[4]; 105 106#ifdef SDL_HAVE_YUV 107 // YV12 texture support 108 bool yuv; 109 D3D_TextureRep utexture; 110 D3D_TextureRep vtexture; 111 Uint8 *pixels; 112 int pitch; 113 SDL_Rect locked_rect; 114#endif 115} D3D_TextureData; 116 117typedef struct 118{ 119 float x, y, z; 120 DWORD color; 121 float u, v; 122} Vertex; 123 124static bool D3D_SetError(const char *prefix, HRESULT result) 125{ 126 const char *error; 127 128 switch (result) { 129 case D3DERR_WRONGTEXTUREFORMAT: 130 error = "WRONGTEXTUREFORMAT"; 131 break; 132 case D3DERR_UNSUPPORTEDCOLOROPERATION: 133 error = "UNSUPPORTEDCOLOROPERATION"; 134 break; 135 case D3DERR_UNSUPPORTEDCOLORARG: 136 error = "UNSUPPORTEDCOLORARG"; 137 break; 138 case D3DERR_UNSUPPORTEDALPHAOPERATION: 139 error = "UNSUPPORTEDALPHAOPERATION"; 140 break; 141 case D3DERR_UNSUPPORTEDALPHAARG: 142 error = "UNSUPPORTEDALPHAARG"; 143 break; 144 case D3DERR_TOOMANYOPERATIONS: 145 error = "TOOMANYOPERATIONS"; 146 break; 147 case D3DERR_CONFLICTINGTEXTUREFILTER: 148 error = "CONFLICTINGTEXTUREFILTER"; 149 break; 150 case D3DERR_UNSUPPORTEDFACTORVALUE: 151 error = "UNSUPPORTEDFACTORVALUE"; 152 break; 153 case D3DERR_CONFLICTINGRENDERSTATE: 154 error = "CONFLICTINGRENDERSTATE"; 155 break; 156 case D3DERR_UNSUPPORTEDTEXTUREFILTER: 157 error = "UNSUPPORTEDTEXTUREFILTER"; 158 break; 159 case D3DERR_CONFLICTINGTEXTUREPALETTE: 160 error = "CONFLICTINGTEXTUREPALETTE"; 161 break; 162 case D3DERR_DRIVERINTERNALERROR: 163 error = "DRIVERINTERNALERROR"; 164 break; 165 case D3DERR_NOTFOUND: 166 error = "NOTFOUND"; 167 break; 168 case D3DERR_MOREDATA: 169 error = "MOREDATA"; 170 break; 171 case D3DERR_DEVICELOST: 172 error = "DEVICELOST"; 173 break; 174 case D3DERR_DEVICENOTRESET: 175 error = "DEVICENOTRESET"; 176 break; 177 case D3DERR_NOTAVAILABLE: 178 error = "NOTAVAILABLE"; 179 break; 180 case D3DERR_OUTOFVIDEOMEMORY: 181 error = "OUTOFVIDEOMEMORY"; 182 break; 183 case D3DERR_INVALIDDEVICE: 184 error = "INVALIDDEVICE"; 185 break; 186 case D3DERR_INVALIDCALL: 187 error = "INVALIDCALL"; 188 break; 189 case D3DERR_DRIVERINVALIDCALL: 190 error = "DRIVERINVALIDCALL"; 191 break; 192 case D3DERR_WASSTILLDRAWING: 193 error = "WASSTILLDRAWING"; 194 break; 195 default: 196 error = "UNKNOWN"; 197 break; 198 } 199 return SDL_SetError("%s: %s", prefix, error); 200} 201 202static const struct { 203 Uint32 sdl; 204 D3DFORMAT d3d; 205} d3d_format_map[] = { 206 { SDL_PIXELFORMAT_ARGB8888, D3DFMT_A8R8G8B8 }, 207 { SDL_PIXELFORMAT_XRGB8888, D3DFMT_X8R8G8B8 }, 208 { SDL_PIXELFORMAT_ABGR8888, D3DFMT_A8B8G8R8 }, 209 { SDL_PIXELFORMAT_XBGR8888, D3DFMT_X8B8G8R8 }, 210 { SDL_PIXELFORMAT_ARGB2101010, D3DFMT_A2R10G10B10 }, 211 { SDL_PIXELFORMAT_RGB565, D3DFMT_R5G6B5 }, 212 { SDL_PIXELFORMAT_ARGB1555, D3DFMT_A1R5G5B5 }, 213 { SDL_PIXELFORMAT_XRGB1555, D3DFMT_X1R5G5B5 }, 214 { SDL_PIXELFORMAT_ARGB4444, D3DFMT_A4R4G4B4 }, 215 { SDL_PIXELFORMAT_XRGB4444, D3DFMT_X4R4G4B4 } 216}; 217 218static D3DFORMAT PixelFormatToD3DFMT(Uint32 format) 219{ 220 switch (format) { 221 case SDL_PIXELFORMAT_INDEX8: 222 case SDL_PIXELFORMAT_YV12: 223 case SDL_PIXELFORMAT_IYUV: 224 case SDL_PIXELFORMAT_NV12: 225 case SDL_PIXELFORMAT_NV21: 226 return D3DFMT_L8; 227 default: 228 for (int i = 0; i < SDL_arraysize(d3d_format_map); i++) { 229 if (d3d_format_map[i].sdl == format) { 230 return d3d_format_map[i].d3d; 231 } 232 } 233 return D3DFMT_UNKNOWN; 234 } 235} 236 237static SDL_PixelFormat D3DFMTToPixelFormat(D3DFORMAT format) 238{ 239 for (int i = 0; i < SDL_arraysize(d3d_format_map); i++) { 240 if (d3d_format_map[i].d3d == format) { 241 return d3d_format_map[i].sdl; 242 } 243 } 244 return SDL_PIXELFORMAT_UNKNOWN; 245} 246 247static void D3D_InitRenderState(D3D_RenderData *data) 248{ 249 D3DMATRIX matrix; 250 251 IDirect3DDevice9 *device = data->device; 252 IDirect3DDevice9_SetPixelShader(device, NULL); 253 IDirect3DDevice9_SetTexture(device, 0, NULL); 254 IDirect3DDevice9_SetTexture(device, 1, NULL); 255 IDirect3DDevice9_SetTexture(device, 2, NULL); 256 IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); 257 IDirect3DDevice9_SetVertexShader(device, NULL); 258 IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE); 259 IDirect3DDevice9_SetRenderState(device, D3DRS_CULLMODE, D3DCULL_NONE); 260 IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE); 261 262 // Enable color modulation by diffuse color 263 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLOROP, 264 D3DTOP_MODULATE); 265 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLORARG1, 266 D3DTA_TEXTURE); 267 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLORARG2, 268 D3DTA_DIFFUSE); 269 270 // Enable alpha modulation by diffuse alpha 271 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAOP, 272 D3DTOP_MODULATE); 273 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAARG1, 274 D3DTA_TEXTURE); 275 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAARG2, 276 D3DTA_DIFFUSE); 277 278 // Enable separate alpha blend function, if possible 279 if (data->enableSeparateAlphaBlend) { 280 IDirect3DDevice9_SetRenderState(device, D3DRS_SEPARATEALPHABLENDENABLE, TRUE); 281 } 282 283 // Disable second texture stage, since we're done 284 IDirect3DDevice9_SetTextureStageState(device, 1, D3DTSS_COLOROP, 285 D3DTOP_DISABLE); 286 IDirect3DDevice9_SetTextureStageState(device, 1, D3DTSS_ALPHAOP, 287 D3DTOP_DISABLE); 288 289 // Set an identity world and view matrix 290 SDL_zero(matrix); 291 matrix.m[0][0] = 1.0f; 292 matrix.m[1][1] = 1.0f; 293 matrix.m[2][2] = 1.0f; 294 matrix.m[3][3] = 1.0f; 295 IDirect3DDevice9_SetTransform(device, D3DTS_WORLD, &matrix); 296 IDirect3DDevice9_SetTransform(device, D3DTS_VIEW, &matrix); 297 298 // Reset our current scale mode 299 for (int i = 0; i < SDL_arraysize(data->scaleMode); ++i) { 300 data->scaleMode[i] = SDL_SCALEMODE_INVALID; 301 } 302 303 // Reset our current address mode 304 for (int i = 0; i < SDL_arraysize(data->addressModeU); ++i) { 305 data->addressModeU[i] = SDL_TEXTURE_ADDRESS_INVALID; 306 } 307 for (int i = 0; i < SDL_arraysize(data->addressModeV); ++i) { 308 data->addressModeV[i] = SDL_TEXTURE_ADDRESS_INVALID; 309 } 310 311 // Start the render with beginScene 312 data->beginScene = true; 313} 314 315static bool D3D_Reset(SDL_Renderer *renderer); 316 317static bool D3D_ActivateRenderer(SDL_Renderer *renderer) 318{ 319 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 320 HRESULT result; 321 322 if (data->updateSize) { 323 SDL_Window *window = renderer->window; 324 int w, h; 325 const SDL_DisplayMode *fullscreen_mode = NULL; 326 327 SDL_GetWindowSizeInPixels(window, &w, &h); 328 data->pparams.BackBufferWidth = w; 329 data->pparams.BackBufferHeight = h; 330 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { 331 fullscreen_mode = SDL_GetWindowFullscreenMode(window); 332 } 333 if (fullscreen_mode) { 334 data->pparams.Windowed = FALSE; 335 data->pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode->format); 336 data->pparams.FullScreen_RefreshRateInHz = (UINT)SDL_ceilf(fullscreen_mode->refresh_rate); 337 } else { 338 data->pparams.Windowed = TRUE; 339 data->pparams.BackBufferFormat = D3DFMT_UNKNOWN; 340 data->pparams.FullScreen_RefreshRateInHz = 0; 341 } 342 if (!D3D_Reset(renderer)) { 343 return false; 344 } 345 346 data->updateSize = false; 347 } 348 if (data->beginScene) { 349 result = IDirect3DDevice9_BeginScene(data->device); 350 if (result == D3DERR_DEVICELOST) { 351 if (!D3D_Reset(renderer)) { 352 return false; 353 } 354 result = IDirect3DDevice9_BeginScene(data->device); 355 } 356 if (FAILED(result)) { 357 return D3D_SetError("BeginScene()", result); 358 } 359 data->beginScene = false; 360 } 361 return true; 362} 363 364static void D3D_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) 365{ 366 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 367 368 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { 369 data->updateSize = true; 370 } 371} 372 373static D3DBLEND GetBlendFunc(SDL_BlendFactor factor) 374{ 375 switch (factor) { 376 case SDL_BLENDFACTOR_ZERO: 377 return D3DBLEND_ZERO; 378 case SDL_BLENDFACTOR_ONE: 379 return D3DBLEND_ONE; 380 case SDL_BLENDFACTOR_SRC_COLOR: 381 return D3DBLEND_SRCCOLOR; 382 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: 383 return D3DBLEND_INVSRCCOLOR; 384 case SDL_BLENDFACTOR_SRC_ALPHA: 385 return D3DBLEND_SRCALPHA; 386 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: 387 return D3DBLEND_INVSRCALPHA; 388 case SDL_BLENDFACTOR_DST_COLOR: 389 return D3DBLEND_DESTCOLOR; 390 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: 391 return D3DBLEND_INVDESTCOLOR; 392 case SDL_BLENDFACTOR_DST_ALPHA: 393 return D3DBLEND_DESTALPHA; 394 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: 395 return D3DBLEND_INVDESTALPHA; 396 default: 397 break; 398 } 399 return (D3DBLEND)0; 400} 401 402static D3DBLENDOP GetBlendEquation(SDL_BlendOperation operation) 403{ 404 switch (operation) { 405 case SDL_BLENDOPERATION_ADD: 406 return D3DBLENDOP_ADD; 407 case SDL_BLENDOPERATION_SUBTRACT: 408 return D3DBLENDOP_SUBTRACT; 409 case SDL_BLENDOPERATION_REV_SUBTRACT: 410 return D3DBLENDOP_REVSUBTRACT; 411 case SDL_BLENDOPERATION_MINIMUM: 412 return D3DBLENDOP_MIN; 413 case SDL_BLENDOPERATION_MAXIMUM: 414 return D3DBLENDOP_MAX; 415 default: 416 break; 417 } 418 return (D3DBLENDOP)0; 419} 420 421static bool D3D_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) 422{ 423 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 424 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); 425 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); 426 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); 427 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); 428 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); 429 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); 430 431 if (!GetBlendFunc(srcColorFactor) || !GetBlendFunc(srcAlphaFactor) || 432 !GetBlendEquation(colorOperation) || 433 !GetBlendFunc(dstColorFactor) || !GetBlendFunc(dstAlphaFactor) || 434 !GetBlendEquation(alphaOperation)) { 435 return false; 436 } 437 438 if (!data->enableSeparateAlphaBlend) { 439 if ((srcColorFactor != srcAlphaFactor) || (dstColorFactor != dstAlphaFactor) || (colorOperation != alphaOperation)) { 440 return false; 441 } 442 } 443 return true; 444} 445 446static bool D3D_CreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD usage, Uint32 format, D3DFORMAT d3dfmt, int w, int h) 447{ 448 HRESULT result; 449 450 texture->dirty = false; 451 texture->w = w; 452 texture->h = h; 453 texture->usage = usage; 454 texture->format = format; 455 texture->d3dfmt = d3dfmt; 456 457 result = IDirect3DDevice9_CreateTexture(device, w, h, 1, usage, 458 PixelFormatToD3DFMT(format), 459 D3DPOOL_DEFAULT, &texture->texture, NULL); 460 if (FAILED(result)) { 461 return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result); 462 } 463 return true; 464} 465 466static bool D3D_CreateStagingTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture) 467{ 468 HRESULT result; 469 470 if (!texture->staging) { 471 result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, 0, 472 texture->d3dfmt, D3DPOOL_SYSTEMMEM, &texture->staging, NULL); 473 if (FAILED(result)) { 474 return D3D_SetError("CreateTexture(D3DPOOL_SYSTEMMEM)", result); 475 } 476 } 477 return true; 478} 479 480static bool D3D_RecreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture) 481{ 482 if (texture->texture) { 483 IDirect3DTexture9_Release(texture->texture); 484 texture->texture = NULL; 485 } 486 if (texture->staging) { 487 IDirect3DTexture9_AddDirtyRect(texture->staging, NULL); 488 texture->dirty = true; 489 } 490 return true; 491} 492 493static bool D3D_UpdateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, int x, int y, int w, int h, const void *pixels, int pitch) 494{ 495 RECT d3drect; 496 D3DLOCKED_RECT locked; 497 const Uint8 *src; 498 Uint8 *dst; 499 int row, length; 500 HRESULT result; 501 502 if (!D3D_CreateStagingTexture(device, texture)) { 503 return false; 504 } 505 506 d3drect.left = x; 507 d3drect.right = (LONG)x + w; 508 d3drect.top = y; 509 d3drect.bottom = (LONG)y + h; 510 511 result = IDirect3DTexture9_LockRect(texture->staging, 0, &locked, &d3drect, 0); 512 if (FAILED(result)) { 513 return D3D_SetError("LockRect()", result); 514 } 515 516 src = (const Uint8 *)pixels; 517 dst = (Uint8 *)locked.pBits; 518 length = w * SDL_BYTESPERPIXEL(texture->format); 519 if (length == pitch && length == locked.Pitch) { 520 SDL_memcpy(dst, src, (size_t)length * h); 521 } else { 522 if (length > pitch) { 523 length = pitch; 524 } 525 if (length > locked.Pitch) { 526 length = locked.Pitch; 527 } 528 for (row = 0; row < h; ++row) { 529 SDL_memcpy(dst, src, length); 530 src += pitch; 531 dst += locked.Pitch; 532 } 533 } 534 result = IDirect3DTexture9_UnlockRect(texture->staging, 0); 535 if (FAILED(result)) { 536 return D3D_SetError("UnlockRect()", result); 537 } 538 texture->dirty = true; 539 540 return true; 541} 542 543static void D3D_DestroyTextureRep(D3D_TextureRep *texture) 544{ 545 if (texture->texture) { 546 IDirect3DTexture9_Release(texture->texture); 547 texture->texture = NULL; 548 } 549 if (texture->staging) { 550 IDirect3DTexture9_Release(texture->staging); 551 texture->staging = NULL; 552 } 553} 554 555static bool D3D_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 556{ 557 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 558 D3D_PaletteData *palettedata = (D3D_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); 559 if (!palettedata) { 560 return false; 561 } 562 palette->internal = palettedata; 563 564 if (!D3D_CreateTextureRep(data->device, &palettedata->texture, 0, SDL_PIXELFORMAT_ARGB8888, D3DFMT_A8R8G8B8, 256, 1)) { 565 SDL_free(palettedata); 566 return false; 567 } 568 569 // Keep a reference to the palette so we can restore the texture if we lose the D3D device 570 if (data->palettes) { 571 palettedata->next = data->palettes; 572 data->palettes->prev = palettedata; 573 } 574 data->palettes = palettedata; 575 return true; 576} 577 578static bool D3D_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) 579{ 580 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 581 D3D_PaletteData *palettedata = (D3D_PaletteData *)palette->internal; 582 bool retval; 583 584 Uint32 *entries = SDL_stack_alloc(Uint32, ncolors); 585 if (!entries) { 586 return false; 587 } 588 for (int i = 0; i < ncolors; ++i) { 589 entries[i] = (colors[i].a << 24) | (colors[i].r << 16) | (colors[i].g << 8) | colors[i].b; 590 } 591 retval = D3D_UpdateTextureRep(data->device, &palettedata->texture, 0, 0, ncolors, 1, entries, ncolors * sizeof(*entries)); 592 SDL_stack_free(entries); 593 return retval; 594} 595 596static void D3D_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 597{ 598 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 599 D3D_PaletteData *palettedata = (D3D_PaletteData *)palette->internal; 600 601 if (palettedata) { 602 D3D_DestroyTextureRep(&palettedata->texture); 603 604 if (data->palettes == palettedata) { 605 data->palettes = palettedata->next; 606 } else if (palettedata->prev) { 607 palettedata->prev->next = palettedata->next; 608 } 609 if (palettedata->next) { 610 palettedata->next->prev = palettedata->prev; 611 } 612 SDL_free(palettedata); 613 } 614} 615 616static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, 617 const SDL_Rect *rect, const void *pixels, int pitch) 618{ 619 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 620 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 621 622 if (!texturedata) { 623 return SDL_SetError("Texture is not currently available"); 624 } 625 626 if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) { 627 return false; 628 } 629#ifdef SDL_HAVE_YUV 630 if (texturedata->yuv) { 631 // Skip to the correct offset into the next texture 632 pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); 633 634 if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { 635 return false; 636 } 637 638 // Skip to the correct offset into the next texture 639 pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); 640 if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { 641 return false; 642 } 643 } 644#endif 645 return true; 646} 647 648static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) 649{ 650 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 651 D3D_TextureData *texturedata; 652 DWORD usage; 653 654 texturedata = (D3D_TextureData *)SDL_calloc(1, sizeof(*texturedata)); 655 if (!texturedata) { 656 return false; 657 } 658 659 texture->internal = texturedata; 660 661 if (texture->access == SDL_TEXTUREACCESS_TARGET) { 662 usage = D3DUSAGE_RENDERTARGET; 663 } else { 664 usage = 0; 665 } 666 667 if (!D3D_CreateTextureRep(data->device, &texturedata->texture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) { 668 return false; 669 } 670 if (texture->format == SDL_PIXELFORMAT_INDEX8) { 671 texturedata->shader_params_length = 1; // The palette shader takes 1 float4 parameters 672 texturedata->shader_params = texturedata->palette_shader_params; 673 texturedata->palette_shader_params[0] = 1.0f / texture->w; 674 texturedata->palette_shader_params[1] = 1.0f / texture->h; 675 texturedata->palette_shader_params[2] = texture->w; 676 texturedata->palette_shader_params[3] = texture->h; 677 } 678#ifdef SDL_HAVE_YUV 679 if (texture->format == SDL_PIXELFORMAT_YV12 || 680 texture->format == SDL_PIXELFORMAT_IYUV) { 681 texturedata->yuv = true; 682 683 if (!D3D_CreateTextureRep(data->device, &texturedata->utexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2)) { 684 return false; 685 } 686 687 if (!D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2)) { 688 return false; 689 } 690 691 texturedata->shader_params_length = 4; // The YUV shader takes 4 float4 parameters 692 texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); 693 if (texturedata->shader_params == NULL) { 694 return SDL_SetError("Unsupported YUV colorspace"); 695 } 696 } 697#endif 698 return true; 699} 700 701static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) 702{ 703 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 704 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 705 706 if (!texturedata) { 707 return true; 708 } 709 710 if (!D3D_RecreateTextureRep(data->device, &texturedata->texture)) { 711 return false; 712 } 713#ifdef SDL_HAVE_YUV 714 if (texturedata->yuv) { 715 if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) { 716 return false; 717 } 718 719 if (!D3D_RecreateTextureRep(data->device, &texturedata->vtexture)) { 720 return false; 721 } 722 } 723#endif 724 return true; 725} 726 727#ifdef SDL_HAVE_YUV 728static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, 729 const SDL_Rect *rect, 730 const Uint8 *Yplane, int Ypitch, 731 const Uint8 *Uplane, int Upitch, 732 const Uint8 *Vplane, int Vpitch) 733{ 734 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 735 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 736 737 if (!texturedata) { 738 return SDL_SetError("Texture is not currently available"); 739 } 740 741 if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) { 742 return false; 743 } 744 if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) { 745 return false; 746 } 747 if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) { 748 return false; 749 } 750 return true; 751} 752#endif 753 754static bool D3D_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, 755 const SDL_Rect *rect, void **pixels, int *pitch) 756{ 757 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 758 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 759 IDirect3DDevice9 *device = data->device; 760 761 if (!texturedata) { 762 return SDL_SetError("Texture is not currently available"); 763 } 764#ifdef SDL_HAVE_YUV 765 texturedata->locked_rect = *rect; 766 767 if (texturedata->yuv) { 768 // It's more efficient to upload directly... 769 if (!texturedata->pixels) { 770 texturedata->pitch = texture->w; 771 texturedata->pixels = (Uint8 *)SDL_malloc((texture->h * texturedata->pitch * 3) / 2); 772 if (!texturedata->pixels) { 773 return false; 774 } 775 } 776 *pixels = 777 (void *)(texturedata->pixels + rect->y * texturedata->pitch + 778 rect->x * SDL_BYTESPERPIXEL(texture->format)); 779 *pitch = texturedata->pitch; 780 } else 781#endif 782 { 783 RECT d3drect; 784 D3DLOCKED_RECT locked; 785 HRESULT result; 786 787 if (!D3D_CreateStagingTexture(device, &texturedata->texture)) { 788 return false; 789 } 790 791 d3drect.left = rect->x; 792 d3drect.right = (LONG)rect->x + rect->w; 793 d3drect.top = rect->y; 794 d3drect.bottom = (LONG)rect->y + rect->h; 795 796 result = IDirect3DTexture9_LockRect(texturedata->texture.staging, 0, &locked, &d3drect, 0); 797 if (FAILED(result)) { 798 return D3D_SetError("LockRect()", result); 799 } 800 *pixels = locked.pBits; 801 *pitch = locked.Pitch; 802 } 803 return true; 804} 805 806static void D3D_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) 807{ 808 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 809 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 810 811 if (!texturedata) { 812 return; 813 } 814#ifdef SDL_HAVE_YUV 815 if (texturedata->yuv) { 816 const SDL_Rect *rect = &texturedata->locked_rect; 817 void *pixels = 818 (void *)(texturedata->pixels + rect->y * texturedata->pitch + 819 rect->x * SDL_BYTESPERPIXEL(texture->format)); 820 D3D_UpdateTexture(renderer, texture, rect, pixels, texturedata->pitch); 821 } else 822#endif 823 { 824 IDirect3DTexture9_UnlockRect(texturedata->texture.staging, 0); 825 texturedata->texture.dirty = true; 826 if (data->drawstate.texture == texture) { 827 data->drawstate.texture = NULL; 828 data->drawstate.shader = SHADER_NONE; 829 data->drawstate.shader_params = NULL; 830 data->drawstate.texture_state_dirty = false; 831 IDirect3DDevice9_SetPixelShader(data->device, NULL); 832 IDirect3DDevice9_SetTexture(data->device, 0, NULL); 833 } 834 } 835} 836 837static bool D3D_SetRenderTargetInternal(SDL_Renderer *renderer, SDL_Texture *texture) 838{ 839 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 840 D3D_TextureData *texturedata; 841 D3D_TextureRep *texturerep; 842 HRESULT result; 843 IDirect3DDevice9 *device = data->device; 844 845 // Release the previous render target if it wasn't the default one 846 if (data->currentRenderTarget) { 847 IDirect3DSurface9_Release(data->currentRenderTarget); 848 data->currentRenderTarget = NULL; 849 } 850 851 if (!texture) { 852 IDirect3DDevice9_SetRenderTarget(data->device, 0, data->defaultRenderTarget); 853 return true; 854 } 855 856 texturedata = (D3D_TextureData *)texture->internal; 857 if (!texturedata) { 858 return SDL_SetError("Texture is not currently available"); 859 } 860 861 // Make sure the render target is updated if it was locked and written to 862 texturerep = &texturedata->texture; 863 if (texturerep->dirty && texturerep->staging) { 864 if (!texturerep->texture) { 865 result = IDirect3DDevice9_CreateTexture(device, texturerep->w, texturerep->h, 1, texturerep->usage, 866 PixelFormatToD3DFMT(texturerep->format), D3DPOOL_DEFAULT, &texturerep->texture, NULL); 867 if (FAILED(result)) { 868 return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result); 869 } 870 } 871 872 result = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texturerep->staging, (IDirect3DBaseTexture9 *)texturerep->texture); 873 if (FAILED(result)) { 874 return D3D_SetError("UpdateTexture()", result); 875 } 876 texturerep->dirty = false; 877 } 878 879 result = IDirect3DTexture9_GetSurfaceLevel(texturedata->texture.texture, 0, &data->currentRenderTarget); 880 if (FAILED(result)) { 881 return D3D_SetError("GetSurfaceLevel()", result); 882 } 883 result = IDirect3DDevice9_SetRenderTarget(data->device, 0, data->currentRenderTarget); 884 if (FAILED(result)) { 885 return D3D_SetError("SetRenderTarget()", result); 886 } 887 888 return true; 889} 890 891static bool D3D_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 892{ 893 if (!D3D_ActivateRenderer(renderer)) { 894 return false; 895 } 896 897 return D3D_SetRenderTargetInternal(renderer, texture); 898} 899 900static bool D3D_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) 901{ 902 return true; // nothing to do in this backend. 903} 904 905static bool D3D_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) 906{ 907 const DWORD color = D3DCOLOR_COLORVALUE(cmd->data.draw.color.r * cmd->data.draw.color_scale, 908 cmd->data.draw.color.g * cmd->data.draw.color_scale, 909 cmd->data.draw.color.b * cmd->data.draw.color_scale, 910 cmd->data.draw.color.a); 911 const size_t vertslen = count * sizeof(Vertex); 912 Vertex *verts = (Vertex *)SDL_AllocateRenderVertices(renderer, vertslen, 0, &cmd->data.draw.first); 913 int i; 914 915 if (!verts) { 916 return false; 917 } 918 919 SDL_memset(verts, '\0', vertslen); 920 cmd->data.draw.count = count; 921 922 for (i = 0; i < count; i++, verts++, points++) { 923 verts->x = points->x; 924 verts->y = points->y; 925 verts->color = color; 926 } 927 928 return true; 929} 930 931static bool D3D_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 932 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, 933 int num_vertices, const void *indices, int num_indices, int size_indices, 934 float scale_x, float scale_y) 935{ 936 int i; 937 int count = indices ? num_indices : num_vertices; 938 Vertex *verts = (Vertex *)SDL_AllocateRenderVertices(renderer, count * sizeof(Vertex), 0, &cmd->data.draw.first); 939 const float color_scale = cmd->data.draw.color_scale; 940 941 if (!verts) { 942 return false; 943 } 944 945 cmd->data.draw.count = count; 946 size_indices = indices ? size_indices : 0; 947 948 for (i = 0; i < count; i++) { 949 int j; 950 float *xy_; 951 SDL_FColor *col_; 952 if (size_indices == 4) { 953 j = ((const Uint32 *)indices)[i]; 954 } else if (size_indices == 2) { 955 j = ((const Uint16 *)indices)[i]; 956 } else if (size_indices == 1) { 957 j = ((const Uint8 *)indices)[i]; 958 } else { 959 j = i; 960 } 961 962 xy_ = (float *)((char *)xy + j * xy_stride); 963 col_ = (SDL_FColor *)((char *)color + j * color_stride); 964 965 verts->x = xy_[0] * scale_x - 0.5f; 966 verts->y = xy_[1] * scale_y - 0.5f; 967 verts->z = 0.0f; 968 verts->color = D3DCOLOR_COLORVALUE(col_->r * color_scale, col_->g * color_scale, col_->b * color_scale, col_->a); 969 970 if (texture) { 971 float *uv_ = (float *)((char *)uv + j * uv_stride); 972 verts->u = uv_[0]; 973 verts->v = uv_[1]; 974 } else { 975 verts->u = 0.0f; 976 verts->v = 0.0f; 977 } 978 979 verts += 1; 980 } 981 return true; 982} 983 984static bool UpdateDirtyTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture) 985{ 986 if (texture->dirty && texture->staging) { 987 HRESULT result; 988 if (!texture->texture) { 989 result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, texture->usage, 990 PixelFormatToD3DFMT(texture->format), D3DPOOL_DEFAULT, &texture->texture, NULL); 991 if (FAILED(result)) { 992 return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result); 993 } 994 } 995 996 result = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texture->staging, (IDirect3DBaseTexture9 *)texture->texture); 997 if (FAILED(result)) { 998 return D3D_SetError("UpdateTexture()", result); 999 } 1000 texture->dirty = false; 1001 } 1002 return true; 1003} 1004 1005static bool BindTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD sampler) 1006{ 1007 HRESULT result; 1008 UpdateDirtyTexture(device, texture); 1009 result = IDirect3DDevice9_SetTexture(device, sampler, (IDirect3DBaseTexture9 *)texture->texture); 1010 if (FAILED(result)) { 1011 return D3D_SetError("SetTexture()", result); 1012 } 1013 return true; 1014} 1015 1016static void UpdateTextureScaleMode(D3D_RenderData *data, SDL_ScaleMode scaleMode, unsigned index) 1017{ 1018 if (scaleMode != data->scaleMode[index]) { 1019 switch (scaleMode) { 1020 case SDL_SCALEMODE_PIXELART: 1021 case SDL_SCALEMODE_NEAREST: 1022 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MINFILTER, D3DTEXF_POINT); 1023 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 1024 break; 1025 case SDL_SCALEMODE_LINEAR: 1026 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); 1027 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 1028 break; 1029 default: 1030 break; 1031 } 1032 data->scaleMode[index] = scaleMode; 1033 } 1034} 1035 1036static DWORD TranslateAddressMode(SDL_TextureAddressMode addressMode) 1037{ 1038 switch (addressMode) { 1039 case SDL_TEXTURE_ADDRESS_CLAMP: 1040 return D3DTADDRESS_CLAMP; 1041 case SDL_TEXTURE_ADDRESS_WRAP: 1042 return D3DTADDRESS_WRAP; 1043 default: 1044 SDL_assert(!"Unknown texture address mode"); 1045 return D3DTADDRESS_CLAMP; 1046 } 1047} 1048 1049static void UpdateTextureAddressMode(D3D_RenderData *data, SDL_TextureAddressMode addressModeU, SDL_TextureAddressMode addressModeV, unsigned index) 1050{ 1051 if (addressModeU != data->addressModeU[index]) { 1052 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_ADDRESSU, TranslateAddressMode(addressModeU)); 1053 data->addressModeU[index] = addressModeU; 1054 } 1055 if (addressModeV != data->addressModeV[index]) { 1056 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_ADDRESSV, TranslateAddressMode(addressModeV)); 1057 data->addressModeV[index] = addressModeV; 1058 } 1059} 1060 1061static bool SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, SDL_ScaleMode scale_mode, D3D9_Shader *shader, const float **shader_params) 1062{ 1063 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 1064 1065 if (!texturedata) { 1066 return SDL_SetError("Texture is not currently available"); 1067 } 1068 1069 if (texture->format == SDL_PIXELFORMAT_INDEX8) { 1070 if (scale_mode == SDL_SCALEMODE_LINEAR) { 1071 *shader = SHADER_PALETTE_LINEAR; 1072 } else { 1073 *shader = SHADER_PALETTE_NEAREST; 1074 } 1075#ifdef SDL_HAVE_YUV 1076 } else if (texturedata->yuv) { 1077 *shader = SHADER_YUV; 1078#endif // SDL_HAVE_YUV 1079 } 1080 *shader_params = texturedata->shader_params; 1081 1082 if (!BindTextureRep(data->device, &texturedata->texture, 0)) { 1083 return false; 1084 } 1085 if (texture->palette) { 1086 D3D_PaletteData *palette = (D3D_PaletteData *)texture->palette->internal; 1087 if (!BindTextureRep(data->device, &palette->texture, 1)) { 1088 return false; 1089 } 1090 } 1091#ifdef SDL_HAVE_YUV 1092 if (texturedata->yuv) { 1093 if (!BindTextureRep(data->device, &texturedata->utexture, 1)) { 1094 return false; 1095 } 1096 if (!BindTextureRep(data->device, &texturedata->vtexture, 2)) { 1097 return false; 1098 } 1099 } 1100#endif 1101 return true; 1102} 1103 1104static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) 1105{ 1106 SDL_Texture *texture = cmd->data.draw.texture; 1107 const SDL_BlendMode blend = cmd->data.draw.blend; 1108 1109 if (texture != data->drawstate.texture || data->drawstate.texture_state_dirty) { 1110 D3D_TextureData *oldtexturedata = data->drawstate.texture ? (D3D_TextureData *)data->drawstate.texture->internal : NULL; 1111 D3D_TextureData *newtexturedata = texture ? (D3D_TextureData *)texture->internal : NULL; 1112 D3D9_Shader shader = SHADER_NONE; 1113 const float *shader_params = NULL; 1114 1115 // disable any enabled textures we aren't going to use, let SetupTextureState() do the rest. 1116 if (!texture) { 1117 IDirect3DDevice9_SetTexture(data->device, 0, NULL); 1118 } 1119 if ((!newtexturedata || !texture->palette) && 1120 ((oldtexturedata && data->drawstate.texture->palette) || data->drawstate.texture_state_dirty)) { 1121 IDirect3DDevice9_SetTexture(data->device, 1, NULL); 1122 } 1123#ifdef SDL_HAVE_YUV 1124 if ((!newtexturedata || !newtexturedata->yuv) && ((oldtexturedata && oldtexturedata->yuv) || data->drawstate.texture_state_dirty)) { 1125 IDirect3DDevice9_SetTexture(data->device, 1, NULL); 1126 IDirect3DDevice9_SetTexture(data->device, 2, NULL); 1127 } 1128#endif 1129 if (texture && !SetupTextureState(data, texture, cmd->data.draw.texture_scale_mode, &shader, &shader_params)) { 1130 return false; 1131 } 1132 1133 if (shader != data->drawstate.shader || data->drawstate.texture_state_dirty) { 1134 const HRESULT result = IDirect3DDevice9_SetPixelShader(data->device, data->shaders[shader]); 1135 if (FAILED(result)) { 1136 return D3D_SetError("IDirect3DDevice9_SetPixelShader()", result); 1137 } 1138 data->drawstate.shader = shader; 1139 } 1140 1141 if (shader_params != data->drawstate.shader_params || data->drawstate.texture_state_dirty) { 1142 if (shader_params) { 1143 const UINT shader_params_length = 4; // The YUV shader takes 4 float4 parameters 1144 const HRESULT result = IDirect3DDevice9_SetPixelShaderConstantF(data->device, 0, shader_params, shader_params_length); 1145 if (FAILED(result)) { 1146 return D3D_SetError("IDirect3DDevice9_SetPixelShaderConstantF()", result); 1147 } 1148 } 1149 data->drawstate.shader_params = shader_params; 1150 } 1151 1152 data->drawstate.texture = texture; 1153 data->drawstate.texture_state_dirty = false; 1154 } else if (texture) { 1155 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 1156 if (texturedata) { 1157 UpdateDirtyTexture(data->device, &texturedata->texture); 1158 if (texture->palette) { 1159 D3D_PaletteData *palettedata = (D3D_PaletteData *)texture->palette->internal; 1160 UpdateDirtyTexture(data->device, &palettedata->texture); 1161 } 1162#ifdef SDL_HAVE_YUV 1163 if (texturedata->yuv) { 1164 UpdateDirtyTexture(data->device, &texturedata->utexture); 1165 UpdateDirtyTexture(data->device, &texturedata->vtexture); 1166 } 1167#endif // SDL_HAVE_YUV 1168 } 1169 } 1170 1171 if (texture) { 1172 UpdateTextureScaleMode(data, cmd->data.draw.texture_scale_mode, 0); 1173 UpdateTextureAddressMode(data, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v, 0); 1174 1175#ifdef SDL_HAVE_YUV 1176 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; 1177 if (texturedata && texturedata->yuv) { 1178 UpdateTextureScaleMode(data, cmd->data.draw.texture_scale_mode, 1); 1179 UpdateTextureScaleMode(data, cmd->data.draw.texture_scale_mode, 2); 1180 UpdateTextureAddressMode(data, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v, 1); 1181 UpdateTextureAddressMode(data, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v, 2); 1182 } 1183#endif // SDL_HAVE_YUV 1184 } 1185 1186 if (blend != data->drawstate.blend) { 1187 if (blend == SDL_BLENDMODE_NONE) { 1188 IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, FALSE); 1189 } else { 1190 IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, TRUE); 1191 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND, 1192 GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend))); 1193 IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND, 1194 GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend))); 1195 IDirect3DDevice9_SetRenderState(data->device, D3DRS_BLENDOP, 1196 GetBlendEquation(SDL_GetBlendModeColorOperation(blend))); 1197 if (data->enableSeparateAlphaBlend) { 1198 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA, 1199 GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend))); 1200 IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA, 1201 GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); 1202 IDirect3DDevice9_SetRenderState(data->device, D3DRS_BLENDOPALPHA, 1203 GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend))); 1204 } 1205 } 1206 1207 data->drawstate.blend = blend; 1208 } 1209 1210 if (data->drawstate.viewport_dirty) { 1211 const SDL_Rect *viewport = &data->drawstate.viewport; 1212 D3DVIEWPORT9 d3dviewport; 1213 d3dviewport.X = viewport->x; 1214 d3dviewport.Y = viewport->y; 1215 d3dviewport.Width = viewport->w; 1216 d3dviewport.Height = viewport->h; 1217 d3dviewport.MinZ = 0.0f; 1218 d3dviewport.MaxZ = 1.0f; 1219 IDirect3DDevice9_SetViewport(data->device, &d3dviewport); 1220 1221 // Set an orthographic projection matrix 1222 if (viewport->w && viewport->h) { 1223 D3DMATRIX d3dmatrix; 1224 SDL_zero(d3dmatrix); 1225 d3dmatrix.m[0][0] = 2.0f / viewport->w; 1226 d3dmatrix.m[1][1] = -2.0f / viewport->h; 1227 d3dmatrix.m[2][2] = 1.0f; 1228 d3dmatrix.m[3][0] = -1.0f; 1229 d3dmatrix.m[3][1] = 1.0f; 1230 d3dmatrix.m[3][3] = 1.0f; 1231 IDirect3DDevice9_SetTransform(data->device, D3DTS_PROJECTION, &d3dmatrix); 1232 } 1233 1234 data->drawstate.viewport_dirty = false; 1235 } 1236 1237 if (data->drawstate.cliprect_enabled_dirty) { 1238 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, data->drawstate.cliprect_enabled ? TRUE : FALSE); 1239 data->drawstate.cliprect_enabled_dirty = false; 1240 } 1241 1242 if (data->drawstate.cliprect_dirty) { 1243 const SDL_Rect *viewport = &data->drawstate.viewport; 1244 const SDL_Rect *rect = &data->drawstate.cliprect; 1245 RECT d3drect; 1246 d3drect.left = (LONG)viewport->x + rect->x; 1247 d3drect.top = (LONG)viewport->y + rect->y; 1248 d3drect.right = (LONG)viewport->x + rect->x + rect->w; 1249 d3drect.bottom = (LONG)viewport->y + rect->y + rect->h; 1250 IDirect3DDevice9_SetScissorRect(data->device, &d3drect); 1251 data->drawstate.cliprect_dirty = false; 1252 } 1253 1254 return true; 1255} 1256 1257static void D3D_InvalidateCachedState(SDL_Renderer *renderer) 1258{ 1259 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1260 data->drawstate.viewport_dirty = true; 1261 data->drawstate.cliprect_enabled_dirty = true; 1262 data->drawstate.cliprect_dirty = true; 1263 data->drawstate.blend = SDL_BLENDMODE_INVALID; 1264 data->drawstate.texture = NULL; 1265 data->drawstate.shader = SHADER_NONE; 1266 data->drawstate.shader_params = NULL; 1267 data->drawstate.texture_state_dirty = true; 1268} 1269 1270static bool D3D_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) 1271{ 1272 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1273 const int vboidx = data->currentVertexBuffer; 1274 IDirect3DVertexBuffer9 *vbo = NULL; 1275 const bool istarget = renderer->target != NULL; 1276 1277 if (!D3D_ActivateRenderer(renderer)) { 1278 return false; 1279 } 1280 1281 if (vertsize > 0) { 1282 // upload the new VBO data for this set of commands. 1283 vbo = data->vertexBuffers[vboidx]; 1284 if (data->vertexBufferSize[vboidx] < vertsize) { 1285 const DWORD usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; 1286 const DWORD fvf = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1; 1287 if (vbo) { 1288 IDirect3DVertexBuffer9_Release(vbo); 1289 } 1290 1291 if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, (UINT)vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) { 1292 vbo = NULL; 1293 } 1294 data->vertexBuffers[vboidx] = vbo; 1295 data->vertexBufferSize[vboidx] = vbo ? vertsize : 0; 1296 } 1297 1298 if (vbo) { 1299 void *ptr; 1300 if (FAILED(IDirect3DVertexBuffer9_Lock(vbo, 0, (UINT)vertsize, &ptr, D3DLOCK_DISCARD))) { 1301 vbo = NULL; // oh well, we'll do immediate mode drawing. :( 1302 } else { 1303 SDL_memcpy(ptr, vertices, vertsize); 1304 if (FAILED(IDirect3DVertexBuffer9_Unlock(vbo))) { 1305 vbo = NULL; // oh well, we'll do immediate mode drawing. :( 1306 } 1307 } 1308 } 1309 1310 // cycle through a few VBOs so D3D has some time with the data before we replace it. 1311 if (vbo) { 1312 data->currentVertexBuffer++; 1313 if (data->currentVertexBuffer >= SDL_arraysize(data->vertexBuffers)) { 1314 data->currentVertexBuffer = 0; 1315 } 1316 } else if (!data->reportedVboProblem) { 1317 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL failed to get a vertex buffer for this Direct3D 9 rendering batch!"); 1318 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Dropping back to a slower method."); 1319 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This might be a brief hiccup, but if performance is bad, this is probably why."); 1320 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This error will not be logged again for this renderer."); 1321 data->reportedVboProblem = true; 1322 } 1323 } 1324 1325 IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, 0, sizeof(Vertex)); 1326 1327 while (cmd) { 1328 switch (cmd->command) { 1329 case SDL_RENDERCMD_SETDRAWCOLOR: 1330 { 1331 break; // this isn't currently used in this render backend. 1332 } 1333 1334 case SDL_RENDERCMD_SETVIEWPORT: 1335 { 1336 SDL_Rect *viewport = &data->drawstate.viewport; 1337 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { 1338 SDL_copyp(viewport, &cmd->data.viewport.rect); 1339 data->drawstate.viewport_dirty = true; 1340 data->drawstate.cliprect_dirty = true; 1341 } 1342 break; 1343 } 1344 1345 case SDL_RENDERCMD_SETCLIPRECT: 1346 { 1347 const SDL_Rect *rect = &cmd->data.cliprect.rect; 1348 if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { 1349 data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; 1350 data->drawstate.cliprect_enabled_dirty = true; 1351 } 1352 1353 if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof(*rect)) != 0) { 1354 SDL_copyp(&data->drawstate.cliprect, rect); 1355 data->drawstate.cliprect_dirty = true; 1356 } 1357 break; 1358 } 1359 1360 case SDL_RENDERCMD_CLEAR: 1361 { 1362 const DWORD color = D3DCOLOR_COLORVALUE(cmd->data.color.color.r * cmd->data.color.color_scale, 1363 cmd->data.color.color.g * cmd->data.color.color_scale, 1364 cmd->data.color.color.b * cmd->data.color.color_scale, 1365 cmd->data.color.color.a); 1366 const SDL_Rect *viewport = &data->drawstate.viewport; 1367 const int backw = istarget ? renderer->target->w : data->pparams.BackBufferWidth; 1368 const int backh = istarget ? renderer->target->h : data->pparams.BackBufferHeight; 1369 const bool viewport_equal = ((viewport->x == 0) && (viewport->y == 0) && (viewport->w == backw) && (viewport->h == backh)); 1370 1371 if (data->drawstate.cliprect_enabled || data->drawstate.cliprect_enabled_dirty) { 1372 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); 1373 data->drawstate.cliprect_enabled_dirty = data->drawstate.cliprect_enabled; 1374 } 1375 1376 // Don't reset the viewport if we don't have to! 1377 if (!data->drawstate.viewport_dirty && viewport_equal) { 1378 IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); 1379 } else { 1380 // Clear is defined to clear the entire render target 1381 D3DVIEWPORT9 wholeviewport = { 0, 0, 0, 0, 0.0f, 1.0f }; 1382 wholeviewport.Width = backw; 1383 wholeviewport.Height = backh; 1384 IDirect3DDevice9_SetViewport(data->device, &wholeviewport); 1385 data->drawstate.viewport_dirty = true; // we still need to (re)set orthographic projection, so always mark it dirty. 1386 IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); 1387 } 1388 1389 break; 1390 } 1391 1392 case SDL_RENDERCMD_DRAW_LINES: 1393 { 1394 size_t count = cmd->data.draw.count; 1395 const size_t first = cmd->data.draw.first; 1396 const size_t start = first / sizeof(Vertex); 1397 const Vertex *verts = (Vertex *)(((Uint8 *)vertices) + first); 1398 1399 SetDrawState(data, cmd); 1400 1401 // Add the final point in the line 1402 size_t line_start = 0; 1403 size_t line_end = line_start + count - 1; 1404 if (count == 2 || verts[line_start].x != verts[line_end].x || verts[line_start].y != verts[line_end].y) { 1405 if (vbo) { 1406 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT)(start + line_end), 1); 1407 } else { 1408 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, &verts[line_end], sizeof(Vertex)); 1409 } 1410 } 1411 1412 if (count > 2) { 1413 // joined lines cannot be grouped 1414 if (vbo) { 1415 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, (UINT)start, (UINT)(count - 1)); 1416 } else { 1417 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, (UINT)(count - 1), verts, sizeof(Vertex)); 1418 } 1419 } else { 1420 // let's group non joined lines 1421 SDL_RenderCommand *finalcmd = cmd; 1422 SDL_RenderCommand *nextcmd; 1423 SDL_BlendMode thisblend = cmd->data.draw.blend; 1424 1425 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) { 1426 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 1427 if (nextcmdtype != SDL_RENDERCMD_DRAW_LINES) { 1428 if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) { 1429 // The vertex data has the draw color built in, ignore this 1430 continue; 1431 } 1432 break; // can't go any further on this draw call, different render command up next. 1433 } else if (nextcmd->data.draw.count != 2) { 1434 break; // can't go any further on this draw call, those are joined lines 1435 } else if (nextcmd->data.draw.blend != thisblend) { 1436 break; // can't go any further on this draw call, different blendmode copy up next. 1437 } else { 1438 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 1439 1440 // Add the final point in the line 1441 line_start = count; 1442 line_end = line_start + nextcmd->data.draw.count - 1; 1443 if (verts[line_start].x != verts[line_end].x || verts[line_start].y != verts[line_end].y) { 1444 if (vbo) { 1445 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT)(start + line_end), 1); 1446 } else { 1447 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, &verts[line_end], sizeof(Vertex)); 1448 } 1449 } 1450 count += nextcmd->data.draw.count; 1451 } 1452 } 1453 1454 if (vbo) { 1455 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINELIST, (UINT)start, (UINT)(count - 1)); 1456 } else { 1457 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINELIST, (UINT)(count - 1), verts, sizeof(Vertex)); 1458 } 1459 cmd = finalcmd; // skip any copy commands we just combined in here. 1460 } 1461 break; 1462 } 1463 1464 case SDL_RENDERCMD_FILL_RECTS: // unused 1465 break; 1466 1467 case SDL_RENDERCMD_COPY: // unused 1468 break; 1469 1470 case SDL_RENDERCMD_COPY_EX: // unused 1471 break; 1472 1473 case SDL_RENDERCMD_DRAW_POINTS: 1474 case SDL_RENDERCMD_GEOMETRY: 1475 { 1476 /* as long as we have the same copy command in a row, with the 1477 same texture, we can combine them all into a single draw call. */ 1478 SDL_Texture *thistexture = cmd->data.draw.texture; 1479 SDL_BlendMode thisblend = cmd->data.draw.blend; 1480 SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode; 1481 SDL_TextureAddressMode thisaddressmode_u = cmd->data.draw.texture_address_mode_u; 1482 SDL_TextureAddressMode thisaddressmode_v = cmd->data.draw.texture_address_mode_v; 1483 const SDL_RenderCommandType thiscmdtype = cmd->command; 1484 SDL_RenderCommand *finalcmd = cmd; 1485 SDL_RenderCommand *nextcmd; 1486 size_t count = cmd->data.draw.count; 1487 const size_t first = cmd->data.draw.first; 1488 const size_t start = first / sizeof(Vertex); 1489 const Vertex *verts = (Vertex *)(((Uint8 *)vertices) + first); 1490 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) { 1491 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 1492 if (nextcmdtype != thiscmdtype) { 1493 if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) { 1494 // The vertex data has the draw color built in, ignore this 1495 continue; 1496 } 1497 break; // can't go any further on this draw call, different render command up next. 1498 } else if (nextcmd->data.draw.texture != thistexture || 1499 nextcmd->data.draw.texture_scale_mode != thisscalemode || 1500 nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u || 1501 nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v || 1502 nextcmd->data.draw.blend != thisblend) { 1503 break; // can't go any further on this draw call, different texture/blendmode copy up next. 1504 } else { 1505 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 1506 count += nextcmd->data.draw.count; 1507 } 1508 } 1509 1510 SetDrawState(data, cmd); 1511 1512 if (thiscmdtype == SDL_RENDERCMD_GEOMETRY) { 1513 if (vbo) { 1514 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLELIST, (UINT)start, (UINT)count / 3); 1515 } else { 1516 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLELIST, (UINT)count / 3, verts, sizeof(Vertex)); 1517 } 1518 } else { 1519 if (vbo) { 1520 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT)start, (UINT)count); 1521 } else { 1522 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, (UINT)count, verts, sizeof(Vertex)); 1523 } 1524 } 1525 cmd = finalcmd; // skip any copy commands we just combined in here. 1526 break; 1527 } 1528 1529 case SDL_RENDERCMD_NO_OP: 1530 break; 1531 } 1532 1533 cmd = cmd->next; 1534 } 1535 1536 return true; 1537} 1538 1539static SDL_Surface *D3D_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) 1540{ 1541 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1542 D3DSURFACE_DESC desc; 1543 LPDIRECT3DSURFACE9 backBuffer; 1544 LPDIRECT3DSURFACE9 surface; 1545 RECT d3drect; 1546 D3DLOCKED_RECT locked; 1547 HRESULT result; 1548 SDL_Surface *output; 1549 1550 if (data->currentRenderTarget) { 1551 backBuffer = data->currentRenderTarget; 1552 } else { 1553 backBuffer = data->defaultRenderTarget; 1554 } 1555 1556 result = IDirect3DSurface9_GetDesc(backBuffer, &desc); 1557 if (FAILED(result)) { 1558 D3D_SetError("GetDesc()", result); 1559 return NULL; 1560 } 1561 1562 result = IDirect3DDevice9_CreateOffscreenPlainSurface(data->device, desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surface, NULL); 1563 if (FAILED(result)) { 1564 D3D_SetError("CreateOffscreenPlainSurface()", result); 1565 return NULL; 1566 } 1567 1568 result = IDirect3DDevice9_GetRenderTargetData(data->device, backBuffer, surface); 1569 if (FAILED(result)) { 1570 IDirect3DSurface9_Release(surface); 1571 D3D_SetError("GetRenderTargetData()", result); 1572 return NULL; 1573 } 1574 1575 d3drect.left = rect->x; 1576 d3drect.right = (LONG)rect->x + rect->w; 1577 d3drect.top = rect->y; 1578 d3drect.bottom = (LONG)rect->y + rect->h; 1579 1580 result = IDirect3DSurface9_LockRect(surface, &locked, &d3drect, D3DLOCK_READONLY); 1581 if (FAILED(result)) { 1582 IDirect3DSurface9_Release(surface); 1583 D3D_SetError("LockRect()", result); 1584 return NULL; 1585 } 1586 1587 output = SDL_DuplicatePixels(rect->w, rect->h, D3DFMTToPixelFormat(desc.Format), SDL_COLORSPACE_SRGB, locked.pBits, locked.Pitch); 1588 1589 IDirect3DSurface9_UnlockRect(surface); 1590 1591 IDirect3DSurface9_Release(surface); 1592 1593 return output; 1594} 1595 1596static bool D3D_RenderPresent(SDL_Renderer *renderer) 1597{ 1598 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1599 HRESULT result; 1600 1601 if (!data->beginScene) { 1602 IDirect3DDevice9_EndScene(data->device); 1603 data->beginScene = true; 1604 } 1605 1606 result = IDirect3DDevice9_TestCooperativeLevel(data->device); 1607 if (result == D3DERR_DEVICELOST) { 1608 // We'll reset later 1609 return false; 1610 } 1611 if (result == D3DERR_DEVICENOTRESET) { 1612 D3D_Reset(renderer); 1613 } 1614 result = IDirect3DDevice9_Present(data->device, NULL, NULL, NULL, NULL); 1615 if (FAILED(result)) { 1616 return D3D_SetError("Present()", result); 1617 } 1618 return true; 1619} 1620 1621static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) 1622{ 1623 D3D_RenderData *renderdata = (D3D_RenderData *)renderer->internal; 1624 D3D_TextureData *data = (D3D_TextureData *)texture->internal; 1625 1626 if (renderdata->drawstate.texture == texture) { 1627 renderdata->drawstate.texture = NULL; 1628 renderdata->drawstate.shader = SHADER_NONE; 1629 renderdata->drawstate.shader_params = NULL; 1630 renderdata->drawstate.texture_state_dirty = false; 1631 IDirect3DDevice9_SetPixelShader(renderdata->device, NULL); 1632 IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL); 1633 if (texture->palette) { 1634 IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL); 1635 } 1636#ifdef SDL_HAVE_YUV 1637 if (data && data->yuv) { 1638 IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL); 1639 IDirect3DDevice9_SetTexture(renderdata->device, 2, NULL); 1640 } 1641#endif 1642 } 1643 1644 if (!data) { 1645 return; 1646 } 1647 1648 D3D_DestroyTextureRep(&data->texture); 1649#ifdef SDL_HAVE_YUV 1650 D3D_DestroyTextureRep(&data->utexture); 1651 D3D_DestroyTextureRep(&data->vtexture); 1652 SDL_free(data->pixels); 1653#endif 1654 SDL_free(data); 1655 texture->internal = NULL; 1656} 1657 1658static void D3D_DestroyRenderer(SDL_Renderer *renderer) 1659{ 1660 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1661 1662 if (data) { 1663 int i; 1664 1665 // Make sure the palettes have been freed 1666 SDL_assert(!data->palettes); 1667 1668 // Release the render target 1669 if (data->defaultRenderTarget) { 1670 IDirect3DSurface9_Release(data->defaultRenderTarget); 1671 data->defaultRenderTarget = NULL; 1672 } 1673 if (data->currentRenderTarget) { 1674 IDirect3DSurface9_Release(data->currentRenderTarget); 1675 data->currentRenderTarget = NULL; 1676 } 1677 for (i = 0; i < SDL_arraysize(data->shaders); ++i) { 1678 if (data->shaders[i]) { 1679 IDirect3DPixelShader9_Release(data->shaders[i]); 1680 data->shaders[i] = NULL; 1681 } 1682 } 1683 // Release all vertex buffers 1684 for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) { 1685 if (data->vertexBuffers[i]) { 1686 IDirect3DVertexBuffer9_Release(data->vertexBuffers[i]); 1687 } 1688 data->vertexBuffers[i] = NULL; 1689 } 1690 if (data->device) { 1691 IDirect3DDevice9_Release(data->device); 1692 data->device = NULL; 1693 } 1694 if (data->d3d) { 1695 IDirect3D9_Release(data->d3d); 1696 SDL_UnloadObject(data->d3dDLL); 1697 } 1698 SDL_free(data); 1699 } 1700} 1701 1702static bool D3D_Reset(SDL_Renderer *renderer) 1703{ 1704 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1705 const Float4X4 d3dmatrix = MatrixIdentity(); 1706 HRESULT result; 1707 SDL_Texture *texture; 1708 D3D_PaletteData *palette; 1709 int i; 1710 1711 // Cancel any scene that we've started 1712 if (!data->beginScene) { 1713 IDirect3DDevice9_EndScene(data->device); 1714 data->beginScene = true; 1715 } 1716 1717 // Release the default render target before reset 1718 if (data->defaultRenderTarget) { 1719 IDirect3DSurface9_Release(data->defaultRenderTarget); 1720 data->defaultRenderTarget = NULL; 1721 } 1722 if (data->currentRenderTarget) { 1723 IDirect3DSurface9_Release(data->currentRenderTarget); 1724 data->currentRenderTarget = NULL; 1725 } 1726 1727 // Release application render targets 1728 for (texture = renderer->textures; texture; texture = texture->next) { 1729 if (texture->access == SDL_TEXTUREACCESS_TARGET) { 1730 D3D_DestroyTexture(renderer, texture); 1731 } else { 1732 D3D_RecreateTexture(renderer, texture); 1733 } 1734 } 1735 1736 // Release all palettes 1737 for (palette = data->palettes; palette; palette = palette->next) { 1738 D3D_RecreateTextureRep(data->device, &palette->texture); 1739 } 1740 1741 // Release all vertex buffers 1742 for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) { 1743 if (data->vertexBuffers[i]) { 1744 IDirect3DVertexBuffer9_Release(data->vertexBuffers[i]); 1745 } 1746 data->vertexBuffers[i] = NULL; 1747 data->vertexBufferSize[i] = 0; 1748 } 1749 1750 result = IDirect3DDevice9_Reset(data->device, &data->pparams); 1751 if (FAILED(result)) { 1752 if (result == D3DERR_DEVICELOST) { 1753 // Don't worry about it, we'll reset later... 1754 return true; 1755 } else { 1756 return D3D_SetError("Reset()", result); 1757 } 1758 } 1759 1760 // Allocate application render targets 1761 for (texture = renderer->textures; texture; texture = texture->next) { 1762 if (texture->access == SDL_TEXTUREACCESS_TARGET) { 1763 D3D_CreateTexture(renderer, texture, 0); 1764 } 1765 } 1766 1767 IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget); 1768 D3D_InitRenderState(data); 1769 D3D_SetRenderTargetInternal(renderer, renderer->target); 1770 1771 D3D_InvalidateCachedState(renderer); 1772 1773 IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX *)&d3dmatrix); 1774 1775 // Let the application know that render targets were reset 1776 { 1777 SDL_Event event; 1778 SDL_zero(event); 1779 event.type = SDL_EVENT_RENDER_TARGETS_RESET; 1780 event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer)); 1781 SDL_PushEvent(&event); 1782 } 1783 1784 return true; 1785} 1786 1787static bool D3D_SetVSync(SDL_Renderer *renderer, const int vsync) 1788{ 1789 D3D_RenderData *data = (D3D_RenderData *)renderer->internal; 1790 1791 DWORD PresentationInterval; 1792 switch (vsync) { 1793 case 0: 1794 PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; 1795 break; 1796 case 1: 1797 PresentationInterval = D3DPRESENT_INTERVAL_ONE; 1798 break; 1799 case 2: 1800 PresentationInterval = D3DPRESENT_INTERVAL_TWO; 1801 break; 1802 case 3: 1803 PresentationInterval = D3DPRESENT_INTERVAL_THREE; 1804 break; 1805 case 4: 1806 PresentationInterval = D3DPRESENT_INTERVAL_FOUR; 1807 break; 1808 default: 1809 return SDL_Unsupported(); 1810 } 1811 1812 D3DCAPS9 caps; 1813 HRESULT result = IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps); 1814 if (FAILED(result)) { 1815 return D3D_SetError("GetDeviceCaps()", result); 1816 } 1817 if (!(caps.PresentationIntervals & PresentationInterval)) { 1818 return SDL_Unsupported(); 1819 } 1820 data->pparams.PresentationInterval = PresentationInterval; 1821 1822 if (!D3D_Reset(renderer)) { 1823 // D3D_Reset will call SDL_SetError() 1824 return false; 1825 } 1826 return true; 1827} 1828 1829static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props) 1830{ 1831 D3D_RenderData *data; 1832 HRESULT result; 1833 HWND hwnd; 1834 D3DDISPLAYMODE displayMode; 1835 D3DPRESENT_PARAMETERS pparams; 1836 IDirect3DSwapChain9 *chain; 1837 D3DCAPS9 caps; 1838 DWORD device_flags; 1839 int w, h; 1840 SDL_DisplayID displayID; 1841 const SDL_DisplayMode *fullscreen_mode = NULL; 1842 1843 hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); 1844 if (!hwnd) { 1845 return SDL_SetError("Couldn't get window handle"); 1846 } 1847 1848 SDL_SetupRendererColorspace(renderer, create_props); 1849 1850 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { 1851 return SDL_SetError("Unsupported output colorspace"); 1852 } 1853 1854 data = (D3D_RenderData *)SDL_calloc(1, sizeof(*data)); 1855 if (!data) { 1856 return false; 1857 } 1858 1859 if (!D3D_LoadDLL(&data->d3dDLL, &data->d3d)) { 1860 SDL_free(data); 1861 return SDL_SetError("Unable to create Direct3D interface"); 1862 } 1863 1864 renderer->WindowEvent = D3D_WindowEvent; 1865 renderer->SupportsBlendMode = D3D_SupportsBlendMode; 1866 renderer->CreatePalette = D3D_CreatePalette; 1867 renderer->UpdatePalette = D3D_UpdatePalette; 1868 renderer->DestroyPalette = D3D_DestroyPalette; 1869 renderer->CreateTexture = D3D_CreateTexture; 1870 renderer->UpdateTexture = D3D_UpdateTexture; 1871#ifdef SDL_HAVE_YUV 1872 renderer->UpdateTextureYUV = D3D_UpdateTextureYUV; 1873#endif 1874 renderer->LockTexture = D3D_LockTexture; 1875 renderer->UnlockTexture = D3D_UnlockTexture; 1876 renderer->SetRenderTarget = D3D_SetRenderTarget; 1877 renderer->QueueSetViewport = D3D_QueueNoOp; 1878 renderer->QueueSetDrawColor = D3D_QueueNoOp; 1879 renderer->QueueDrawPoints = D3D_QueueDrawPoints; 1880 renderer->QueueDrawLines = D3D_QueueDrawPoints; // lines and points queue vertices the same way. 1881 renderer->QueueGeometry = D3D_QueueGeometry; 1882 renderer->InvalidateCachedState = D3D_InvalidateCachedState; 1883 renderer->RunCommandQueue = D3D_RunCommandQueue; 1884 renderer->RenderReadPixels = D3D_RenderReadPixels; 1885 renderer->RenderPresent = D3D_RenderPresent; 1886 renderer->DestroyTexture = D3D_DestroyTexture; 1887 renderer->DestroyRenderer = D3D_DestroyRenderer; 1888 renderer->SetVSync = D3D_SetVSync; 1889 renderer->internal = data; 1890 D3D_InvalidateCachedState(renderer); 1891 1892 renderer->name = D3D_RenderDriver.name; 1893 1894 SDL_GetWindowSizeInPixels(window, &w, &h); 1895 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { 1896 fullscreen_mode = SDL_GetWindowFullscreenMode(window); 1897 } 1898 1899 SDL_zero(pparams); 1900 pparams.hDeviceWindow = hwnd; 1901 pparams.BackBufferWidth = w; 1902 pparams.BackBufferHeight = h; 1903 pparams.BackBufferCount = 1; 1904 pparams.SwapEffect = D3DSWAPEFFECT_DISCARD; 1905 1906 if (fullscreen_mode) { 1907 pparams.Windowed = FALSE; 1908 pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode->format); 1909 pparams.FullScreen_RefreshRateInHz = (UINT)SDL_ceilf(fullscreen_mode->refresh_rate); 1910 } else { 1911 pparams.Windowed = TRUE; 1912 pparams.BackBufferFormat = D3DFMT_UNKNOWN; 1913 pparams.FullScreen_RefreshRateInHz = 0; 1914 } 1915 pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; 1916 1917 // Get the adapter for the display that the window is on 1918 displayID = SDL_GetDisplayForWindow(window); 1919 data->adapter = SDL_GetDirect3D9AdapterIndex(displayID); 1920 1921 result = IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps); 1922 if (FAILED(result)) { 1923 return D3D_SetError("GetDeviceCaps()", result); 1924 } 1925 1926 device_flags = D3DCREATE_FPU_PRESERVE; 1927 if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) { 1928 device_flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; 1929 } else { 1930 device_flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; 1931 } 1932 if (caps.TextureCaps & D3DPTEXTURECAPS_POW2) { 1933 if (caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) { 1934 renderer->npot_texture_wrap_unsupported = true; 1935 } else { 1936 return SDL_SetError("Non-power-of-two textures are not supported"); 1937 } 1938 } 1939 1940 if (SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, false)) { 1941 device_flags |= D3DCREATE_MULTITHREADED; 1942 } 1943 1944 result = IDirect3D9_CreateDevice(data->d3d, data->adapter, 1945 D3DDEVTYPE_HAL, 1946 pparams.hDeviceWindow, 1947 device_flags, 1948 &pparams, &data->device); 1949 if (FAILED(result)) { 1950 return D3D_SetError("CreateDevice()", result); 1951 } 1952 1953 // Get presentation parameters to fill info 1954 result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain); 1955 if (FAILED(result)) { 1956 return D3D_SetError("GetSwapChain()", result); 1957 } 1958 result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams); 1959 if (FAILED(result)) { 1960 IDirect3DSwapChain9_Release(chain); 1961 return D3D_SetError("GetPresentParameters()", result); 1962 } 1963 IDirect3DSwapChain9_Release(chain); 1964 data->pparams = pparams; 1965 1966 IDirect3DDevice9_GetDeviceCaps(data->device, &caps); 1967 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, SDL_min(caps.MaxTextureWidth, caps.MaxTextureHeight)); 1968 1969 if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) { 1970 data->enableSeparateAlphaBlend = true; 1971 } 1972 1973 // Store the default render target 1974 IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget); 1975 data->currentRenderTarget = NULL; 1976 1977 // Detect the supported texture formats 1978 IDirect3D9_GetAdapterDisplayMode(data->d3d, data->adapter, &displayMode); 1979 for (int i = 0; i < SDL_arraysize(d3d_format_map); i++) { 1980 if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(data->d3d, 1981 data->adapter, 1982 D3DDEVTYPE_HAL, 1983 displayMode.Format, 1984 0, 1985 D3DRTYPE_TEXTURE, 1986 d3d_format_map[i].d3d))) { 1987 SDL_AddSupportedTextureFormat(renderer, d3d_format_map[i].sdl); 1988 } 1989 } 1990 1991 // Set up parameters for rendering 1992 D3D_InitRenderState(data); 1993 for (int i = SHADER_NONE + 1; i < SDL_arraysize(data->shaders); ++i) { 1994 result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]); 1995 if (FAILED(result)) { 1996 D3D_SetError("CreatePixelShader()", result); 1997 } 1998 } 1999 if (caps.MaxSimultaneousTextures >= 2 && 2000 data->shaders[SHADER_PALETTE_NEAREST] && 2001 data->shaders[SHADER_PALETTE_LINEAR]) { 2002 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); 2003 } 2004#ifdef SDL_HAVE_YUV 2005 if (caps.MaxSimultaneousTextures >= 3 && data->shaders[SHADER_YUV]) { 2006 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); 2007 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); 2008 } 2009#endif 2010 2011 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D9_DEVICE_POINTER, data->device); 2012 2013 return true; 2014} 2015 2016SDL_RenderDriver D3D_RenderDriver = { 2017 D3D_CreateRenderer, "direct3d" 2018}; 2019#endif // SDL_VIDEO_RENDER_D3D 2020[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.