diff --git a/Content/Shaders/Quad.flax b/Content/Shaders/Quad.flax index 65ce310fe..6bdf6ce8e 100644 --- a/Content/Shaders/Quad.flax +++ b/Content/Shaders/Quad.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94f68ef9c2d7bc7d453fdd432f85f4643ae3be1bcf839bc1d5d81803d6b2ce7c -size 3505 +oid sha256:60680a4ce8deee4de81d8aafed454fb5aace3a6d18a11eda3b96e81ccaf801a9 +size 4443 diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index c2aa0c82d..bdacb9766 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -297,6 +297,7 @@ struct GPUDevice::PrivateData GPUPipelineState* PS_CopyLinear = nullptr; GPUPipelineState* PS_Clear = nullptr; GPUPipelineState* PS_DecodeYUY2 = nullptr; + GPUPipelineState* PS_DecodeNV12 = nullptr; GPUBuffer* FullscreenTriangleVB = nullptr; AssetReference DefaultMaterial; SoftAssetReference DefaultDeformableMaterial; @@ -715,6 +716,18 @@ GPUPipelineState* GPUDevice::GetDecodeYUY2PS() const return _res->PS_DecodeYUY2; } +GPUPipelineState* GPUDevice::GetDecodeNV12PS() const +{ + if (_res->PS_DecodeNV12 == nullptr) + { + auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; + psDesc.PS = QuadShader->GetPS("PS_DecodeNV12"); + _res->PS_DecodeNV12 = const_cast(this)->CreatePipelineState(); + _res->PS_DecodeNV12->Init(psDesc); + } + return _res->PS_DecodeNV12; +} + GPUBuffer* GPUDevice::GetFullscreenTriangleVB() const { return _res->FullscreenTriangleVB; diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h index f1dfecf60..081f22d03 100644 --- a/Source/Engine/Graphics/GPUDevice.h +++ b/Source/Engine/Graphics/GPUDevice.h @@ -275,6 +275,11 @@ public: /// GPUPipelineState* GetDecodeYUY2PS() const; + /// + /// Gets the shader pipeline state object for NV12 frame decoding to RGBA. + /// + GPUPipelineState* GetDecodeNV12PS() const; + /// /// Gets the fullscreen-triangle vertex buffer. /// diff --git a/Source/Engine/Video/MF/VideoBackendMF.cpp b/Source/Engine/Video/MF/VideoBackendMF.cpp index b1f57bd6a..ef020b858 100644 --- a/Source/Engine/Video/MF/VideoBackendMF.cpp +++ b/Source/Engine/Video/MF/VideoBackendMF.cpp @@ -253,7 +253,7 @@ namespace MF IMF2DBuffer* buffer2D = nullptr; BYTE* bufferData = nullptr; LONG bufferStride = 0; - if (isVideo && sample->GetBufferByIndex(0, &buffer) == S_OK && buffer->QueryInterface(IID_PPV_ARGS(&buffer2D)) == S_OK) + if (isVideo && player.Format != PixelFormat::NV12 && sample->GetBufferByIndex(0, &buffer) == S_OK && buffer->QueryInterface(IID_PPV_ARGS(&buffer2D)) == S_OK) { LONG bufferPitch = 0; hr = buffer2D->Lock2D(&bufferData, &bufferPitch); diff --git a/Source/Engine/Video/Video.cpp b/Source/Engine/Video/Video.cpp index 6b98941ff..20bf55e0d 100644 --- a/Source/Engine/Video/Video.cpp +++ b/Source/Engine/Video/Video.cpp @@ -91,8 +91,19 @@ protected: context->GPU->SetViewportAndScissors((float)_player->Width, (float)_player->Height); context->GPU->SetRenderTarget(frame->View()); context->GPU->BindSR(0, _player->FrameUpload->View()); - ASSERT_LOW_LAYER(_player->Format == PixelFormat::YUY2); - context->GPU->SetState(GPUDevice::Instance->GetDecodeYUY2PS()); + GPUPipelineState* pso; + switch (_player->Format) + { + case PixelFormat::YUY2: + pso = GPUDevice::Instance->GetDecodeYUY2PS(); + break; + case PixelFormat::NV12: + pso = GPUDevice::Instance->GetDecodeNV12PS(); + break; + default: + return Result::Failed; + } + context->GPU->SetState(pso); context->GPU->DrawFullscreenTriangle(); } else diff --git a/Source/Shaders/Quad.shader b/Source/Shaders/Quad.shader index 117a21d1a..0a6c2ba80 100644 --- a/Source/Shaders/Quad.shader +++ b/Source/Shaders/Quad.shader @@ -83,12 +83,21 @@ float PS_DepthCopy(Quad_VS2PS input) : SV_Depth #endif +float4 yuv2rgb(int y, int u, int v) +{ + u -= 128; + v -= 128; + float r = y + 1.402 * v; + float g = y - 0.34414 * u - 0.71414 * v; + float b = y + 1.772 * u; + return float4(r, g, b, 256.0f) / 256.0f; +} + #ifdef _PS_DecodeYUY2 // Raw memory with texture of format YUY2 and size passed in Color.xy Buffer SourceYUY2 : register(t0); -// Pixel Shader for copying depth buffer META_PS(true, FEATURE_LEVEL_ES2) float4 PS_DecodeYUY2(Quad_VS2PS input) : SV_Target { @@ -97,17 +106,39 @@ float4 PS_DecodeYUY2(Quad_VS2PS input) : SV_Target uint data = SourceYUY2[p / 2]; // Unpack YUY components - uint v = (data & 0xff000000) >> 24; - uint y1 = (data & 0xff0000) >> 16; - uint u = (data & 0xff00) >> 8; - uint y0 = data & 0x000000FF; - uint y = p % 2 == 0 ? y0: y1; + int v = ((data & 0xff000000) >> 24); + int y1 = (data & 0xff0000) >> 16; + int u = ((data & 0xff00) >> 8); + int y0 = data & 0xff; + int y = p % 2 == 0 ? y0: y1; // Convert yuv to rgb - float r = (y + 1.402 * (v - 128.0)); - float g = (y - 0.344 * (u - 128.0) - 0.714 * (v - 128.0)); - float b = (y + 1.772 * (u - 128.0)); - return float4(r, g, b, 256.0f) / 256.0f; + return yuv2rgb(y, u, v); +} + +#endif + +#ifdef _PS_DecodeNV12 + +// Raw memory with texture of format NV12 and size passed in Color.xy +Buffer SourceNV12 : register(t0); + +META_PS(true, FEATURE_LEVEL_ES2) +float4 PS_DecodeNV12(Quad_VS2PS input) : SV_Target +{ + // Read NV12 pixel (Y plane of size w*h, followed by interleaved UV plane is of size w*h/2) + uint size = (uint)(Color.x * Color.y); + uint p = (uint)input.Position.y * (uint)Color.x + (uint)input.Position.x; + uint y = (SourceNV12[p / 4] >> ((p % 4) * 8)) & 0xff; + p = (uint)(input.Position.y * 0.5f) * (uint)Color.x + (uint)input.Position.x; + p = (p / 2) * 2; + uint u = (SourceNV12[size / 4 + p / 4] >> ((p % 4) * 8)) & 0xff; + p = (uint)(input.Position.y * 0.5f) * (uint)Color.x + (uint)input.Position.x; + p = (p / 2) * 2 + 1; + uint v = (SourceNV12[size / 4 + p / 4] >> ((p % 4) * 8)) & 0xff; + + // Convert yuv to rgb + return yuv2rgb(y, u, v); } #endif