From 09304831f600c4bde992125dd7d910673c547aa7 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 4 Dec 2025 07:59:19 -0800 Subject: [PATCH] Fixed issues with YUV texture updates in the 2D renderer --- src/render/direct3d11/SDL_render_d3d11.c | 24 +++++---- src/render/direct3d12/SDL_render_d3d12.c | 6 +-- src/render/gpu/SDL_render_gpu.c | 10 ++-- src/render/vulkan/SDL_render_vulkan.c | 67 ++++++++++++++---------- 4 files changed, 64 insertions(+), 43 deletions(-) diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 3890488055..532329e573 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -1585,22 +1585,25 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, #ifdef SDL_HAVE_YUV if (textureData->nv12) { - const Uint8 *Yplane = (const Uint8 *)srcPixels; - const Uint8 *UVplane = Yplane + rect->h * srcPitch; + int UVbpp = SDL_BYTESPERPIXEL(texture->format) * 2; + int Ypitch = srcPitch; + int UVpitch = (srcPitch + (UVbpp - 1)) & ~(UVbpp - 1); + const Uint8 *plane0 = (const Uint8 *)srcPixels; + const Uint8 *plane1 = plane0 + rect->h * srcPitch; - return D3D11_UpdateTextureNV(renderer, texture, rect, Yplane, srcPitch, UVplane, srcPitch); + return D3D11_UpdateTextureNV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch); } else if (textureData->yuv) { int Ypitch = srcPitch; int UVpitch = ((Ypitch + 1) / 2); - const Uint8 *Yplane = (const Uint8 *)srcPixels; - const Uint8 *Uplane = Yplane + rect->h * Ypitch; - const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch; + const Uint8 *plane0 = (const Uint8 *)srcPixels; + const Uint8 *plane1 = plane0 + rect->h * Ypitch; + const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch; if (texture->format == SDL_PIXELFORMAT_YV12) { - return D3D11_UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Vplane, UVpitch, Uplane, UVpitch); + return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch); } else { - return D3D11_UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, UVpitch, Vplane, UVpitch); + return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch); } } #endif @@ -1652,6 +1655,7 @@ static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, HRESULT result; D3D11_TEXTURE2D_DESC stagingTextureDesc; D3D11_MAPPED_SUBRESOURCE textureMemory; + int bpp = SDL_BYTESPERPIXEL(texture->format); if (!textureData) { return SDL_SetError("Texture is not currently available"); @@ -1695,7 +1699,7 @@ static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, src = Yplane; dst = (Uint8 *)textureMemory.pData; - length = w; + length = w * bpp; if (length == (UINT)Ypitch && length == textureMemory.RowPitch) { SDL_memcpy(dst, src, (size_t)length * h); } else { @@ -1713,7 +1717,7 @@ static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, } src = UVplane; - length = w; + length = ((w + 1) / 2) * 2 * bpp; h = (h + 1) / 2; if (stagingTextureDesc.Format == DXGI_FORMAT_P010) { length = (length + 3) & ~3; diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 032ae4daee..43260ae555 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -2087,10 +2087,10 @@ static bool D3D12_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) { return false; } - if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainResourceStateU)) { + if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch, &textureData->mainResourceStateU)) { return false; } - if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) { + if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) { return false; } if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) { @@ -2115,7 +2115,7 @@ static bool D3D12_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) { return false; } - if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, rect->w, rect->h, UVplane, UVpitch, &textureData->mainResourceState)) { + if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, UVplane, UVpitch, &textureData->mainResourceState)) { return false; } if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) { diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index 9ef9993d88..06a06ee966 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -522,7 +522,6 @@ static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons if (texture->format == SDL_PIXELFORMAT_P010) { UVpitch = (pitch + 3) & ~3; } else { - bpp = 1; UVpitch = (pitch + 1) & ~1; } retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureNV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch); @@ -534,8 +533,13 @@ static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons const Uint8 *Uplane = Yplane + rect->h * Ypitch; const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch; - retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch); - retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch); + if (texture->format == SDL_PIXELFORMAT_YV12) { + retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch); + retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch); + } else { + retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch); + retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch); + } } #endif diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 562a9e2986..0dd5f74a13 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -440,6 +440,8 @@ static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat, int plane) return 1; case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: return (plane == 0) ? 1 : 2; + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: + return (plane == 0) ? 2 : 4; default: return 4; } @@ -2708,8 +2710,8 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S samplerCreateInfo.magFilter = VK_FILTER_NEAREST; samplerCreateInfo.minFilter = VK_FILTER_NEAREST; samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.mipLodBias = 0.0f; samplerCreateInfo.anisotropyEnable = VK_FALSE; @@ -2911,6 +2913,19 @@ static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImag return true; } +#ifdef SDL_HAVE_YUV +static bool VULKAN_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *UVplane, int UVpitch); + +static bool VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *Uplane, int Upitch, + const Uint8 *Vplane, int Vpitch); +#endif + static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *srcPixels, int srcPitch) @@ -2922,38 +2937,36 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return SDL_SetError("Texture is not currently available"); } - if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) { - return false; - } #ifdef SDL_HAVE_YUV Uint32 numPlanes = VULKAN_VkFormatGetNumPlanes(textureData->mainImage.format); - // Skip to the correct offset into the next texture - srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); - // YUV data - if (numPlanes == 3) { - for (Uint32 plane = 1; plane < numPlanes; plane++) { - if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, plane, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, &textureData->mainImage.imageLayout)) { - return false; - } + if (numPlanes == 2) { + // NV12/NV21 data + int UVbpp = VULKAN_GetBytesPerPixel(textureData->mainImage.format, 1); + int Ypitch = srcPitch; + int UVpitch = (srcPitch + (UVbpp - 1)) & ~(UVbpp - 1); + const Uint8 *plane0 = (const Uint8 *)srcPixels; + const Uint8 *plane1 = plane0 + rect->h * srcPitch; - // Skip to the correct offset into the next texture - srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2)); - } - } - // NV12/NV21 data - else if (numPlanes == 2) - { - if (texture->format == SDL_PIXELFORMAT_P010) { - srcPitch = (srcPitch + 3) & ~3; + return VULKAN_UpdateTextureNV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch); + + } else if (numPlanes == 3) { + // YUV data + int Ypitch = srcPitch; + int UVpitch = ((Ypitch + 1) / 2); + const Uint8 *plane0 = (const Uint8 *)srcPixels; + const Uint8 *plane1 = plane0 + rect->h * Ypitch; + const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch; + + if (texture->format == SDL_PIXELFORMAT_YV12) { + return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch); } else { - srcPitch = (srcPitch + 1) & ~1; - } - - if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) { - return false; + return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch); } } #endif + if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) { + return false; + } return true; }