diff --git a/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml b/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml
index fe169e1..a715bb3 100644
--- a/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml
+++ b/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml
@@ -21,8 +21,8 @@
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center"/>
ReadNextPacket(packet))
{
- OutputDebugStringA("[DEBUG] No more packets - playback completed\n");
+ // End of video - stop playback
m_isPlaying = false;
- if (m_playbackTimer) {
- m_playbackTimer.Stop();
- }
+ if (m_playbackTimer) m_playbackTimer.Stop();
UpdateStatus(L"Playback completed");
return;
}
- OutputDebugStringA(("[DEBUG] Packet read - size: " + std::to_string(packet.size) + "\n").c_str());
VideoFrame frame;
- OutputDebugStringA("[DEBUG] Decoding frame...\n");
- if (!m_decoder->DecodeFrame(packet, frame))
- {
- OutputDebugStringA("[DEBUG] Frame decode failed - skipping\n");
- return;
- }
- OutputDebugStringA(("[DEBUG] Frame decoded - size: " + std::to_string(frame.width) + "x" + std::to_string(frame.height) + "\n").c_str());
+ if (!m_decoder->DecodeFrame(packet, frame)) return; // Skip failed frames
RenderFrameToScreen(frame);
m_currentFrame++;
m_currentTime = m_currentFrame / m_frameRate;
- OutputDebugStringA(("[DEBUG] Frame " + std::to_string(m_currentFrame) + " processed\n").c_str());
}
void VideoPlayerControl::ProcessSingleFrameLegacy()
@@ -618,7 +602,16 @@ namespace winrt::Vav2Player::implementation
void VideoPlayerControl::RenderFrameToScreen(const VideoFrame& frame)
{
- OutputDebugStringA("[DEBUG] RenderFrameToScreen() called\n");
+ // GPU rendering attempt
+ if (m_useHardwareRendering && m_gpuRenderer && m_gpuRenderer->IsInitialized()) {
+ if (m_gpuRenderer->TryRenderFrame(frame)) {
+ // GPU rendering successful - apply AspectFit to SwapChainPanel
+ UpdateVideoImageAspectFit(frame.width, frame.height);
+ return; // Success - done
+ }
+ }
+
+ // CPU rendering fallback (always works)
RenderFrameSoftware(frame);
}
@@ -759,10 +752,29 @@ namespace winrt::Vav2Player::implementation
}
else
{
- // Hardware rendering setup (Phase 2 - future)
+ // Hardware rendering setup
VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
- OutputDebugStringA("[DEBUG] Hardware rendering not implemented yet\n");
+
+ if (!m_gpuRenderer)
+ {
+ m_gpuRenderer = std::make_unique();
+ }
+
+ // Initialize GPU renderer with SwapChainPanel
+ HRESULT hr = m_gpuRenderer->Initialize(VideoSwapChainPanel(), 1920, 1080);
+ if (SUCCEEDED(hr))
+ {
+ OutputDebugStringA("[DEBUG] GPU rendering initialized successfully\n");
+ }
+ else
+ {
+ OutputDebugStringA("[DEBUG] GPU rendering initialization failed, falling back to CPU\n");
+ m_useHardwareRendering = false;
+ m_gpuRenderer.reset();
+ VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
+ VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
+ }
}
}
@@ -828,8 +840,14 @@ namespace winrt::Vav2Player::implementation
displayWidth = containerHeight * videoAspectRatio;
}
+ // Apply AspectFit to both CPU and GPU rendering controls
VideoImage().Width(displayWidth);
VideoImage().Height(displayHeight);
+
+ // Also apply to GPU rendering SwapChainPanel
+ VideoSwapChainPanel().Width(displayWidth);
+ VideoSwapChainPanel().Height(displayHeight);
+
OutputDebugStringA(("[DEBUG] Video size set to: " + std::to_string(displayWidth) + "x" + std::to_string(displayHeight) + "\n").c_str());
}
diff --git a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp b/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp
index 82134cf..e31db96 100644
--- a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp
+++ b/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp
@@ -10,8 +10,15 @@
namespace Vav2Player {
SimpleGPURenderer::SimpleGPURenderer()
+ : m_frameIndex(0) // Always use frame index 0 for simplicity
+ , m_fenceValue(0)
{
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ // Initialize frame completion tracking
+ for (UINT i = 0; i < FrameCount; ++i)
+ {
+ m_frameCompletionValues[i] = 0;
+ }
}
SimpleGPURenderer::~SimpleGPURenderer()
@@ -80,13 +87,19 @@ void SimpleGPURenderer::Shutdown()
m_computePipelineState.Reset();
m_computeRootSignature.Reset();
m_srvUavHeap.Reset();
- m_rgbTexture.Reset();
- m_yTexture.Reset();
- m_uTexture.Reset();
- m_vTexture.Reset();
- m_yUploadBuffer.Reset();
- m_uUploadBuffer.Reset();
- m_vUploadBuffer.Reset();
+ for (UINT i = 0; i < FrameCount; ++i)
+ {
+ m_rgbTextures[i].Reset();
+ m_yTextures[i].Reset();
+ m_uTextures[i].Reset();
+ m_vTextures[i].Reset();
+ }
+ for (UINT i = 0; i < FrameCount; ++i)
+ {
+ m_yUploadBuffers[i].Reset();
+ m_uUploadBuffers[i].Reset();
+ m_vUploadBuffers[i].Reset();
+ }
m_constantBuffer.Reset();
for (UINT i = 0; i < FrameCount; i++)
@@ -117,29 +130,53 @@ HRESULT SimpleGPURenderer::RenderVideoFrame(const VideoFrame& frame)
HRESULT hr = S_OK;
- // 1. Create/update video textures if needed
+ // 1. TRIPLE BUFFERING DEBUG: Check frame completion status
+ std::cout << "[SimpleGPURenderer] Frame " << m_frameIndex << " - Current fence: " << m_fence->GetCompletedValue()
+ << ", Target: " << m_frameCompletionValues[m_frameIndex] << std::endl;
+
+ WaitForFrameCompletion(m_frameIndex);
+
+ // 2. Create/update video textures if needed
if (frame.width != m_videoWidth || frame.height != m_videoHeight)
{
hr = CreateVideoTextures(frame.width, frame.height);
if (FAILED(hr)) return hr;
}
- // 2. Upload YUV data to GPU
- hr = UpdateVideoTextures(frame);
+ // 3. Execute complete GPU pipeline in single command recording session
+ hr = ExecuteGPUPipeline(frame);
if (FAILED(hr)) return hr;
- // 3. Execute YUV-to-RGB compute shader
- hr = ExecuteComputeShader();
+ // 4. Signal completion for current frame and advance to next frame
+ const UINT64 currentFrameFenceValue = ++m_fenceValue;
+ m_frameCompletionValues[m_frameIndex] = currentFrameFenceValue;
+ hr = m_commandQueue->Signal(m_fence.Get(), currentFrameFenceValue);
if (FAILED(hr)) return hr;
- // 4. Copy RGB result to back buffer
- hr = CopyToBackBuffer();
- if (FAILED(hr)) return hr;
+ // 5. Advance to next frame (triple buffering rotation)
+ m_frameIndex = (m_frameIndex + 1) % FrameCount;
- std::cout << "[SimpleGPURenderer] Frame rendered (" << frame.width << "x" << frame.height << ")" << std::endl;
+ std::cout << "[SimpleGPURenderer] Frame rendered successfully (" << frame.width << "x" << frame.height << "), frame index: " << m_frameIndex << std::endl;
return S_OK;
}
+bool SimpleGPURenderer::TryRenderFrame(const VideoFrame& frame)
+{
+ try {
+ HRESULT hr = RenderVideoFrame(frame);
+ if (SUCCEEDED(hr))
+ {
+ hr = Present();
+ return SUCCEEDED(hr);
+ }
+ return false;
+ }
+ catch (...) {
+ std::cout << "[SimpleGPURenderer] Exception caught in TryRenderFrame - falling back to CPU" << std::endl;
+ return false;
+ }
+}
+
HRESULT SimpleGPURenderer::Present()
{
if (!m_initialized || !m_swapChain)
@@ -250,9 +287,9 @@ HRESULT SimpleGPURenderer::CreateDescriptorHeaps()
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
- // Create SRV/UAV descriptor heap for compute shader
+ // Create SRV/UAV descriptor heap for compute shader (triple buffering)
D3D12_DESCRIPTOR_HEAP_DESC srvUavHeapDesc = {};
- srvUavHeapDesc.NumDescriptors = 10; // Y,U,V textures + RGB output + spare
+ srvUavHeapDesc.NumDescriptors = FrameCount * 4; // 3 frames * (Y,U,V,RGB) = 12 descriptors
srvUavHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvUavHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
@@ -301,11 +338,9 @@ HRESULT SimpleGPURenderer::CreateSynchronizationObjects()
return hr;
}
- // Initialize fence values
- for (UINT i = 0; i < FrameCount; i++)
- {
- m_fenceValues[i] = 1;
- }
+ // Initialize fence values (already done in constructor)
+ // m_fenceValue = 0;
+ // m_frameCompletionValues already initialized to 0
// Create command allocators
for (UINT i = 0; i < FrameCount; i++)
@@ -405,47 +440,56 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
- hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
- D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_yTexture));
- if (FAILED(hr))
+ // Create video textures for all frames (triple buffering)
+ for (UINT i = 0; i < FrameCount; ++i)
{
- std::cout << "[SimpleGPURenderer] Failed to create Y texture: 0x" << std::hex << hr << std::endl;
- return hr;
+ // Y texture (full resolution)
+ textureDesc.Width = videoWidth;
+ textureDesc.Height = videoHeight;
+ hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&m_yTextures[i]));
+ if (FAILED(hr))
+ {
+ std::cout << "[SimpleGPURenderer] Failed to create Y texture " << i << ": 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
+
+ // U texture (half resolution for 4:2:0)
+ textureDesc.Width = videoWidth / 2;
+ textureDesc.Height = videoHeight / 2;
+ hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&m_uTextures[i]));
+ if (FAILED(hr))
+ {
+ std::cout << "[SimpleGPURenderer] Failed to create U texture " << i << ": 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
+
+ // V texture (half resolution for 4:2:0)
+ hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&m_vTextures[i]));
+ if (FAILED(hr))
+ {
+ std::cout << "[SimpleGPURenderer] Failed to create V texture " << i << ": 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
}
- // Create U texture (half resolution for 4:2:0)
- textureDesc.Width = videoWidth / 2;
- textureDesc.Height = videoHeight / 2;
-
- hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
- D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_uTexture));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer] Failed to create U texture: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Create V texture (half resolution for 4:2:0)
- hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
- D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_vTexture));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer] Failed to create V texture: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Create RGB output texture (for compute shader output)
+ // Create RGB output textures (for compute shader output) - triple buffered
textureDesc.Width = m_width;
textureDesc.Height = m_height;
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
textureDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
- hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
- D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&m_rgbTexture));
- if (FAILED(hr))
+ for (UINT i = 0; i < FrameCount; ++i)
{
- std::cout << "[SimpleGPURenderer] Failed to create RGB texture: 0x" << std::hex << hr << std::endl;
- return hr;
+ hr = m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&m_rgbTextures[i]));
+ if (FAILED(hr))
+ {
+ std::cout << "[SimpleGPURenderer] Failed to create RGB texture " << i << ": 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
}
// Create upload buffers for CPU->GPU transfer
@@ -461,63 +505,221 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
uploadDesc.SampleDesc.Count = 1;
uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
- // Y upload buffer
- uploadDesc.Width = videoWidth * videoHeight;
- hr = m_device->CreateCommittedResource(&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
- D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_yUploadBuffer));
- if (FAILED(hr))
+ // Create upload buffers for all frames (triple buffering)
+ for (UINT i = 0; i < FrameCount; ++i)
{
- std::cout << "[SimpleGPURenderer] Failed to create Y upload buffer: 0x" << std::hex << hr << std::endl;
- return hr;
+ // Y upload buffer
+ uploadDesc.Width = videoWidth * videoHeight;
+ hr = m_device->CreateCommittedResource(&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_yUploadBuffers[i]));
+ if (FAILED(hr))
+ {
+ std::cout << "[SimpleGPURenderer] Failed to create Y upload buffer " << i << ": 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
+
+ // U and V upload buffers
+ uploadDesc.Width = (videoWidth / 2) * (videoHeight / 2);
+ hr = m_device->CreateCommittedResource(&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_uUploadBuffers[i]));
+ if (FAILED(hr)) return hr;
+
+ hr = m_device->CreateCommittedResource(&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_vUploadBuffers[i]));
+ if (FAILED(hr)) return hr;
}
-
- // U and V upload buffers
- uploadDesc.Width = (videoWidth / 2) * (videoHeight / 2);
- hr = m_device->CreateCommittedResource(&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
- D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_uUploadBuffer));
if (FAILED(hr)) return hr;
- hr = m_device->CreateCommittedResource(&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
- D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_vUploadBuffer));
- if (FAILED(hr)) return hr;
+ // Create descriptor views (SRV for Y,U,V textures and UAV for RGB texture) - All frames for triple buffering
+ if (m_srvUavHeap)
+ {
+ CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(m_srvUavHeap->GetCPUDescriptorHandleForHeapStart());
+ UINT descriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Format = DXGI_FORMAT_R8_UNORM;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = 1;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
+ uavDesc.Texture2D.MipSlice = 0;
+
+ // Create descriptors for all frames (triple buffering)
+ for (UINT frameIdx = 0; frameIdx < FrameCount; ++frameIdx)
+ {
+ // Y texture SRV (frame 0: descriptor 0, frame 1: descriptor 4, frame 2: descriptor 8)
+ CD3DX12_CPU_DESCRIPTOR_HANDLE frameHandle = srvHandle;
+ frameHandle.Offset(frameIdx * 4, descriptorSize);
+ m_device->CreateShaderResourceView(m_yTextures[frameIdx].Get(), &srvDesc, frameHandle);
+
+ // U texture SRV (frame 0: descriptor 1, frame 1: descriptor 5, frame 2: descriptor 9)
+ frameHandle.Offset(descriptorSize);
+ m_device->CreateShaderResourceView(m_uTextures[frameIdx].Get(), &srvDesc, frameHandle);
+
+ // V texture SRV (frame 0: descriptor 2, frame 1: descriptor 6, frame 2: descriptor 10)
+ frameHandle.Offset(descriptorSize);
+ m_device->CreateShaderResourceView(m_vTextures[frameIdx].Get(), &srvDesc, frameHandle);
+
+ // RGB texture UAV (frame 0: descriptor 3, frame 1: descriptor 7, frame 2: descriptor 11)
+ frameHandle.Offset(descriptorSize);
+ m_device->CreateUnorderedAccessView(m_rgbTextures[frameIdx].Get(), nullptr, &uavDesc, frameHandle);
+ }
+
+ std::cout << "[SimpleGPURenderer] Descriptor views created for " << FrameCount << " frame sets (triple buffering)" << std::endl;
+ }
+ else
+ {
+ std::cout << "[SimpleGPURenderer] Warning: SRV/UAV heap not available for descriptor creation" << std::endl;
+ }
std::cout << "[SimpleGPURenderer] Video textures created (" << videoWidth << "x" << videoHeight << ")" << std::endl;
return S_OK;
}
-HRESULT SimpleGPURenderer::UpdateVideoTextures(const VideoFrame& frame)
+HRESULT SimpleGPURenderer::ExecuteGPUPipeline(const VideoFrame& frame)
{
- if (!m_yTexture || !m_uTexture || !m_vTexture)
+ if (!m_commandAllocators[m_frameIndex] || !m_commandList)
return E_FAIL;
HRESULT hr = S_OK;
- // Reset command allocator and command list
+ // Step 1: Reset command allocator and list ONCE per frame
hr = m_commandAllocators[m_frameIndex]->Reset();
- if (FAILED(hr)) return hr;
+ if (FAILED(hr)) {
+ std::cout << "[SimpleGPURenderer] Failed to reset command allocator: 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
hr = m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), nullptr);
+ if (FAILED(hr)) {
+ std::cout << "[SimpleGPURenderer] Failed to reset command list: 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
+
+ std::cout << "[SimpleGPURenderer] Starting GPU pipeline for frame " << m_frameIndex << std::endl;
+
+ // Step 2: Update video textures (upload YUV data)
+ hr = UpdateVideoTexturesInternal(frame);
if (FAILED(hr)) return hr;
+ // Step 3: Execute compute shader (YUV->RGB conversion)
+ hr = ExecuteComputeShaderInternal();
+ if (FAILED(hr)) return hr;
+
+ // Step 4: Copy RGB to back buffer
+ hr = CopyToBackBufferInternal();
+ if (FAILED(hr)) return hr;
+
+ // Step 5: Close and execute command list ONCE
+ hr = m_commandList->Close();
+ if (FAILED(hr)) {
+ std::cout << "[SimpleGPURenderer] Failed to close command list: 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
+
+ ID3D12CommandList* commandLists[] = { m_commandList.Get() };
+ m_commandQueue->ExecuteCommandLists(1, commandLists);
+
+ std::cout << "[SimpleGPURenderer] GPU pipeline executed successfully" << std::endl;
+ return S_OK;
+}
+
+HRESULT SimpleGPURenderer::UpdateVideoTexturesInternal(const VideoFrame& frame)
+{
+ // Use current frame's video textures
+ auto& yTexture = m_yTextures[m_frameIndex];
+ auto& uTexture = m_uTextures[m_frameIndex];
+ auto& vTexture = m_vTextures[m_frameIndex];
+ auto& rgbTexture = m_rgbTextures[m_frameIndex];
+
+ if (!yTexture || !uTexture || !vTexture || !rgbTexture)
+ return E_FAIL;
+
+ // TRIPLE BUFFERING: Use current frame's upload buffers (already created during initialization)
+ if (!m_yUploadBuffers[m_frameIndex] || !m_uUploadBuffers[m_frameIndex] || !m_vUploadBuffers[m_frameIndex])
+ return E_FAIL;
+
+ std::cout << "[SimpleGPURenderer] Using upload buffers for frame " << m_frameIndex << std::endl;
+ HRESULT hr = S_OK;
+
+ if (!m_commandAllocators[m_frameIndex] || !m_commandList)
+ return E_FAIL;
+
+ // NOTE: Command allocator and list are reset in ExecuteGPUPipeline() - do not reset here
+
// 1. Map and copy Y data
+ std::cout << "[SimpleGPURenderer] Starting Y data mapping..." << std::endl;
+
+ // Validate device and buffer state
+ if (!m_device) {
+ std::cout << "[SimpleGPURenderer] D3D12 device is null" << std::endl;
+ return E_FAIL;
+ }
+
+ // Use current frame's upload buffers
+ auto& yUploadBuffer = m_yUploadBuffers[m_frameIndex];
+ auto& uUploadBuffer = m_uUploadBuffers[m_frameIndex];
+ auto& vUploadBuffer = m_vUploadBuffers[m_frameIndex];
+
+ // Check upload buffer description
+ D3D12_RESOURCE_DESC bufferDesc = yUploadBuffer->GetDesc();
+ std::cout << "[SimpleGPURenderer] Y buffer size: " << bufferDesc.Width << " bytes" << std::endl;
+
void* yMappedData = nullptr;
- hr = m_yUploadBuffer->Map(0, nullptr, &yMappedData);
+ D3D12_RANGE readRange = { 0, 0 }; // We don't read from this resource on the CPU
+
+ // Additional safety: Check if upload buffer is valid before mapping
+ if (!yUploadBuffer)
+ {
+ std::cout << "[SimpleGPURenderer] Y upload buffer is null!" << std::endl;
+ return E_FAIL;
+ }
+
+ // Frame-specific synchronization has already been handled in RenderVideoFrame()
+ // No additional wait needed here - current frame allocator should be free
+
+ std::cout << "[SimpleGPURenderer] About to call Map() on Y upload buffer..." << std::endl;
+ hr = yUploadBuffer->Map(0, &readRange, &yMappedData);
+ if (FAILED(hr)) {
+ std::cout << "[SimpleGPURenderer] Failed to map Y upload buffer: 0x" << std::hex << hr << std::endl;
+ return hr;
+ }
+
+ if (!yMappedData)
+ {
+ std::cout << "[SimpleGPURenderer] Y upload buffer mapping returned null pointer!" << std::endl;
+ yUploadBuffer->Unmap(0, nullptr);
+ return E_FAIL;
+ }
+
+ // Validate frame data
+ if (!frame.y_plane.get() || frame.width == 0 || frame.height == 0) {
+ std::cout << "[SimpleGPURenderer] Invalid frame data" << std::endl;
+ yUploadBuffer->Unmap(0, nullptr);
+ return E_FAIL;
+ }
+
if (SUCCEEDED(hr))
{
const uint8_t* srcY = frame.y_plane.get();
uint8_t* dstY = static_cast(yMappedData);
+ std::cout << "[SimpleGPURenderer] Copying Y data: " << frame.width << "x" << frame.height << std::endl;
+
for (uint32_t row = 0; row < frame.height; ++row)
{
memcpy(dstY + row * frame.width, srcY + row * frame.y_stride, frame.width);
}
- m_yUploadBuffer->Unmap(0, nullptr);
+ yUploadBuffer->Unmap(0, nullptr);
}
// 2. Map and copy U data
void* uMappedData = nullptr;
- hr = m_uUploadBuffer->Map(0, nullptr, &uMappedData);
+ hr = uUploadBuffer->Map(0, nullptr, &uMappedData);
if (SUCCEEDED(hr))
{
const uint8_t* srcU = frame.u_plane.get();
@@ -530,12 +732,12 @@ HRESULT SimpleGPURenderer::UpdateVideoTextures(const VideoFrame& frame)
memcpy(dstU + row * uvWidth, srcU + row * frame.u_stride, uvWidth);
}
- m_uUploadBuffer->Unmap(0, nullptr);
+ uUploadBuffer->Unmap(0, nullptr);
}
// 3. Map and copy V data
void* vMappedData = nullptr;
- hr = m_vUploadBuffer->Map(0, nullptr, &vMappedData);
+ hr = vUploadBuffer->Map(0, nullptr, &vMappedData);
if (SUCCEEDED(hr))
{
const uint8_t* srcV = frame.v_plane.get();
@@ -548,12 +750,12 @@ HRESULT SimpleGPURenderer::UpdateVideoTextures(const VideoFrame& frame)
memcpy(dstV + row * uvWidth, srcV + row * frame.v_stride, uvWidth);
}
- m_vUploadBuffer->Unmap(0, nullptr);
+ vUploadBuffer->Unmap(0, nullptr);
}
// 4. Copy upload buffers to textures
D3D12_TEXTURE_COPY_LOCATION srcY = {};
- srcY.pResource = m_yUploadBuffer.Get();
+ srcY.pResource = yUploadBuffer.Get();
srcY.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcY.PlacedFootprint.Offset = 0;
srcY.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8_UNORM;
@@ -563,67 +765,71 @@ HRESULT SimpleGPURenderer::UpdateVideoTextures(const VideoFrame& frame)
srcY.PlacedFootprint.Footprint.RowPitch = frame.width;
D3D12_TEXTURE_COPY_LOCATION dstY = {};
- dstY.pResource = m_yTexture.Get();
+ dstY.pResource = yTexture.Get();
dstY.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstY.SubresourceIndex = 0;
- m_commandList->CopyTextureRegion(&dstY, 0, 0, 0, &srcY, nullptr);
-
- // Copy U texture
+ // Prepare U texture copy location
uint32_t uvWidth = frame.width / 2;
uint32_t uvHeight = frame.height / 2;
D3D12_TEXTURE_COPY_LOCATION srcU = srcY;
- srcU.pResource = m_uUploadBuffer.Get();
+ srcU.pResource = uUploadBuffer.Get();
srcU.PlacedFootprint.Footprint.Width = uvWidth;
srcU.PlacedFootprint.Footprint.Height = uvHeight;
srcU.PlacedFootprint.Footprint.RowPitch = uvWidth;
D3D12_TEXTURE_COPY_LOCATION dstU = dstY;
- dstU.pResource = m_uTexture.Get();
+ dstU.pResource = uTexture.Get();
- m_commandList->CopyTextureRegion(&dstU, 0, 0, 0, &srcU, nullptr);
-
- // Copy V texture
+ // Prepare V texture copy location
D3D12_TEXTURE_COPY_LOCATION srcV = srcU;
- srcV.pResource = m_vUploadBuffer.Get();
+ srcV.pResource = vUploadBuffer.Get();
D3D12_TEXTURE_COPY_LOCATION dstV = dstU;
- dstV.pResource = m_vTexture.Get();
+ dstV.pResource = vTexture.Get();
+ // 4.1. First transition textures to COPY_DEST state for writing
+ D3D12_RESOURCE_BARRIER beforeCopyBarriers[3] = {};
+ beforeCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ beforeCopyBarriers[0].Transition.pResource = yTexture.Get();
+ beforeCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
+ beforeCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
+
+ beforeCopyBarriers[1] = beforeCopyBarriers[0];
+ beforeCopyBarriers[1].Transition.pResource = uTexture.Get();
+
+ beforeCopyBarriers[2] = beforeCopyBarriers[0];
+ beforeCopyBarriers[2].Transition.pResource = vTexture.Get();
+
+ m_commandList->ResourceBarrier(3, beforeCopyBarriers);
+
+ // 4.2. Now perform all copy operations
+ m_commandList->CopyTextureRegion(&dstY, 0, 0, 0, &srcY, nullptr);
+ m_commandList->CopyTextureRegion(&dstU, 0, 0, 0, &srcU, nullptr);
m_commandList->CopyTextureRegion(&dstV, 0, 0, 0, &srcV, nullptr);
- // 5. Transition textures to shader resource state
- D3D12_RESOURCE_BARRIER barriers[3] = {};
- barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barriers[0].Transition.pResource = m_yTexture.Get();
- barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
- barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
+ // 5. Transition textures back to shader resource state for reading
+ D3D12_RESOURCE_BARRIER afterCopyBarriers[3] = {};
+ afterCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ afterCopyBarriers[0].Transition.pResource = yTexture.Get();
+ afterCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
+ afterCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
- barriers[1] = barriers[0];
- barriers[1].Transition.pResource = m_uTexture.Get();
+ afterCopyBarriers[1] = afterCopyBarriers[0];
+ afterCopyBarriers[1].Transition.pResource = uTexture.Get();
- barriers[2] = barriers[0];
- barriers[2].Transition.pResource = m_vTexture.Get();
+ afterCopyBarriers[2] = afterCopyBarriers[0];
+ afterCopyBarriers[2].Transition.pResource = vTexture.Get();
- m_commandList->ResourceBarrier(3, barriers);
+ m_commandList->ResourceBarrier(3, afterCopyBarriers);
// Execute commands
- hr = m_commandList->Close();
- if (FAILED(hr)) return hr;
-
- ID3D12CommandList* commandLists[] = { m_commandList.Get() };
- m_commandQueue->ExecuteCommandLists(1, commandLists);
-
- // Wait for completion
- hr = WaitForGPU();
- if (FAILED(hr)) return hr;
-
std::cout << "[SimpleGPURenderer] YUV textures updated (" << frame.width << "x" << frame.height << ")" << std::endl;
return S_OK;
}
-HRESULT SimpleGPURenderer::ExecuteComputeShader()
+HRESULT SimpleGPURenderer::ExecuteComputeShaderInternal()
{
if (!m_computePipelineState || !m_computeRootSignature)
{
@@ -633,12 +839,7 @@ HRESULT SimpleGPURenderer::ExecuteComputeShader()
HRESULT hr = S_OK;
- // Reset command allocator and list
- hr = m_commandAllocators[m_frameIndex]->Reset();
- if (FAILED(hr)) return hr;
-
- hr = m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), nullptr);
- if (FAILED(hr)) return hr;
+ // NOTE: Command allocator and list are reset in ExecuteGPUPipeline() - do not reset here
// Set compute pipeline state
m_commandList->SetComputeRootSignature(m_computeRootSignature.Get());
@@ -648,13 +849,16 @@ HRESULT SimpleGPURenderer::ExecuteComputeShader()
ID3D12DescriptorHeap* heaps[] = { m_srvUavHeap.Get() };
m_commandList->SetDescriptorHeaps(1, heaps);
- // Bind Y, U, V textures as SRVs (Shader Resource Views)
+ // Bind current frame's Y, U, V textures as SRVs (Shader Resource Views)
CD3DX12_GPU_DESCRIPTOR_HANDLE srvHandle(m_srvUavHeap->GetGPUDescriptorHandleForHeapStart());
+ UINT descriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ // Offset to current frame's descriptors (frame 0: offset 0, frame 1: offset 4, frame 2: offset 8)
+ srvHandle.Offset(m_frameIndex * 4, descriptorSize);
m_commandList->SetComputeRootDescriptorTable(0, srvHandle); // Root parameter 0: SRV table
- // Bind RGB texture as UAV (Unordered Access View)
+ // Bind current frame's RGB texture as UAV (Unordered Access View)
CD3DX12_GPU_DESCRIPTOR_HANDLE uavHandle = srvHandle;
- uavHandle.Offset(3, m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
+ uavHandle.Offset(3, descriptorSize); // UAV is at offset +3 from frame's SRV base
m_commandList->SetComputeRootDescriptorTable(1, uavHandle); // Root parameter 1: UAV table
// Dispatch compute shader
@@ -666,65 +870,105 @@ HRESULT SimpleGPURenderer::ExecuteComputeShader()
m_commandList->Dispatch(dispatchX, dispatchY, dispatchZ);
// Add UAV barrier to ensure compute shader completes before next operation
- CD3DX12_RESOURCE_BARRIER uavBarrier = CD3DX12_RESOURCE_BARRIER::UAV(m_rgbTexture.Get());
+ CD3DX12_RESOURCE_BARRIER uavBarrier = CD3DX12_RESOURCE_BARRIER::UAV(m_rgbTextures[m_frameIndex].Get());
m_commandList->ResourceBarrier(1, &uavBarrier);
- // Close and execute command list
- hr = m_commandList->Close();
- if (FAILED(hr)) return hr;
-
- ID3D12CommandList* commandLists[] = { m_commandList.Get() };
- m_commandQueue->ExecuteCommandLists(1, commandLists);
-
- std::cout << "[SimpleGPURenderer] Compute shader executed successfully ("
+ std::cout << "[SimpleGPURenderer] Compute shader commands recorded successfully ("
<< dispatchX << "x" << dispatchY << " thread groups)" << std::endl;
return S_OK;
}
-HRESULT SimpleGPURenderer::CopyToBackBuffer()
+HRESULT SimpleGPURenderer::CopyToBackBufferInternal()
{
- if (!m_renderTargets[m_frameIndex])
+ // Get current back buffer index from swap chain (critical for proper synchronization)
+ UINT currentBackBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
+
+ if (!m_renderTargets[currentBackBufferIndex])
return E_FAIL;
+ std::cout << "[SimpleGPURenderer] CopyToBackBuffer: frameIndex=" << m_frameIndex
+ << ", backBufferIndex=" << currentBackBufferIndex << std::endl;
+
// For Phase 3 testing, we'll just clear the back buffer to a test color
// TODO: Implement actual RGB texture to back buffer copy
- HRESULT hr = m_commandAllocators[m_frameIndex]->Reset();
- if (FAILED(hr)) return hr;
+ // CRITICAL: Do NOT reset command allocator again - it was already reset in UpdateVideoTextures
+ // Just continue using the existing command list
+ HRESULT hr = S_OK;
- hr = m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), nullptr);
- if (FAILED(hr)) return hr;
-
- // Transition back buffer to render target state
+ // Transition back buffer to render target state for clearing
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.pResource = m_renderTargets[m_frameIndex].Get();
+ barrier.Transition.pResource = m_renderTargets[currentBackBufferIndex].Get();
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
m_commandList->ResourceBarrier(1, &barrier);
- // Clear back buffer to test color (green indicates GPU pipeline is working)
- D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
- rtvHandle.ptr += m_frameIndex * m_rtvDescriptorSize;
+ // Clear back buffer to black background
+ CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(),
+ currentBackBufferIndex, m_rtvDescriptorSize);
+ const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; // Black background
+ m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
- const float testColor[4] = { 0.0f, 0.5f, 0.0f, 1.0f }; // Green test color
- m_commandList->ClearRenderTargetView(rtvHandle, testColor, 0, nullptr);
+ // Transition back buffer to copy dest state for texture copy
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
+ m_commandList->ResourceBarrier(1, &barrier);
+
+ // Copy RGB texture to back buffer using simple texture copy (no rendering pipeline needed)
+
+ // Simple approach: transition RGB texture to copy source and use CopyTextureRegion
+ // Transition current frame's RGB texture to copy source
+ D3D12_RESOURCE_BARRIER rgbBarrier = {};
+ rgbBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ rgbBarrier.Transition.pResource = m_rgbTextures[m_frameIndex].Get();
+ rgbBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ rgbBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
+ rgbBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ m_commandList->ResourceBarrier(1, &rgbBarrier);
+
+ // Copy RGB texture region to back buffer (handles format differences)
+ if (m_rgbTextures[m_frameIndex] && m_renderTargets[currentBackBufferIndex])
+ {
+ D3D12_TEXTURE_COPY_LOCATION src = {};
+ src.pResource = m_rgbTextures[m_frameIndex].Get();
+ src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ src.SubresourceIndex = 0;
+
+ D3D12_TEXTURE_COPY_LOCATION dst = {};
+ dst.pResource = m_renderTargets[currentBackBufferIndex].Get();
+ dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dst.SubresourceIndex = 0;
+
+ // Copy the minimum of the two texture sizes to prevent overrun
+ UINT copyWidth = (m_videoWidth < m_width) ? m_videoWidth : m_width;
+ UINT copyHeight = (m_videoHeight < m_height) ? m_videoHeight : m_height;
+
+ D3D12_BOX srcBox = {};
+ srcBox.left = 0;
+ srcBox.top = 0;
+ srcBox.right = copyWidth;
+ srcBox.bottom = copyHeight;
+ srcBox.front = 0;
+ srcBox.back = 1;
+
+ m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, &srcBox);
+ }
+
+ // Transition RGB texture back to UAV for next frame
+ rgbBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
+ rgbBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ m_commandList->ResourceBarrier(1, &rgbBarrier);
// Transition back to present state
- barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
m_commandList->ResourceBarrier(1, &barrier);
- hr = m_commandList->Close();
- if (FAILED(hr)) return hr;
-
- // Execute commands
- ID3D12CommandList* commandLists[] = { m_commandList.Get() };
- m_commandQueue->ExecuteCommandLists(1, commandLists);
-
- std::cout << "[SimpleGPURenderer] Back buffer cleared with test color" << std::endl;
+ std::cout << "[SimpleGPURenderer] RGB texture copy commands recorded ("
+ << m_videoWidth << "x" << m_videoHeight << " -> " << m_width << "x" << m_height << ")" << std::endl;
return S_OK;
}
@@ -733,15 +977,12 @@ HRESULT SimpleGPURenderer::WaitForGPU()
if (!m_commandQueue || !m_fence || !m_fenceEvent)
return E_FAIL;
- // Signal the fence with current value
- const UINT64 fenceValue = m_fenceValues[m_frameIndex];
+ // Simple: signal and wait for current fence value
+ const UINT64 fenceValue = ++m_fenceValue;
HRESULT hr = m_commandQueue->Signal(m_fence.Get(), fenceValue);
if (FAILED(hr)) return hr;
- // Increment fence value for next frame
- m_fenceValues[m_frameIndex]++;
-
- // Wait for fence completion
+ // Wait for completion
if (m_fence->GetCompletedValue() < fenceValue)
{
hr = m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent);
@@ -753,6 +994,32 @@ HRESULT SimpleGPURenderer::WaitForGPU()
return S_OK;
}
+void SimpleGPURenderer::WaitForFrameCompletion(UINT frameIndex)
+{
+ if (!m_fence || !m_fenceEvent)
+ return;
+
+ UINT64 targetValue = m_frameCompletionValues[frameIndex];
+ std::cout << "[SimpleGPURenderer] WaitForFrameCompletion frame " << frameIndex
+ << ": target=" << targetValue << ", current=" << m_fence->GetCompletedValue() << std::endl;
+
+ if (targetValue > 0)
+ {
+ // Always wait with timeout to prevent infinite hangs
+ while (m_fence->GetCompletedValue() < targetValue)
+ {
+ m_fence->SetEventOnCompletion(targetValue, m_fenceEvent);
+ DWORD result = WaitForSingleObject(m_fenceEvent, 1000); // 1 second timeout
+ if (result == WAIT_TIMEOUT)
+ {
+ std::cout << "[SimpleGPURenderer] Frame completion timeout for frame " << frameIndex
+ << ", target: " << targetValue << ", current: " << m_fence->GetCompletedValue() << std::endl;
+ break; // Continue anyway to prevent infinite hang
+ }
+ }
+ }
+}
+
HRESULT SimpleGPURenderer::CreateComputeRootSignature()
{
// Root signature for compute shader: 3 SRVs (Y,U,V) + 1 UAV (RGB) + 1 CBV (constants)
@@ -945,4 +1212,23 @@ HRESULT SimpleGPURenderer::CreateComputePipelineState()
return S_OK;
}
+// Legacy public interface methods (for backward compatibility)
+HRESULT SimpleGPURenderer::UpdateVideoTextures(const VideoFrame& frame)
+{
+ std::cout << "[SimpleGPURenderer] Warning: Using legacy UpdateVideoTextures - use ExecuteGPUPipeline instead" << std::endl;
+ return UpdateVideoTexturesInternal(frame);
+}
+
+HRESULT SimpleGPURenderer::ExecuteComputeShader()
+{
+ std::cout << "[SimpleGPURenderer] Warning: Using legacy ExecuteComputeShader - use ExecuteGPUPipeline instead" << std::endl;
+ return ExecuteComputeShaderInternal();
+}
+
+HRESULT SimpleGPURenderer::CopyToBackBuffer()
+{
+ std::cout << "[SimpleGPURenderer] Warning: Using legacy CopyToBackBuffer - use ExecuteGPUPipeline instead" << std::endl;
+ return CopyToBackBufferInternal();
+}
+
} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.h b/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.h
index 0605b24..c4c2754 100644
--- a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.h
+++ b/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer.h
@@ -29,6 +29,7 @@ public:
// Video rendering
HRESULT RenderVideoFrame(const VideoFrame& frame);
+ bool TryRenderFrame(const VideoFrame& frame); // Returns true if successful
HRESULT Present();
// Size management
@@ -43,16 +44,17 @@ private:
ComPtr m_swapChain;
ComPtr m_rtvHeap;
- // Command objects
- static const UINT FrameCount = 2;
+ // Command objects - Triple buffering
+ static const UINT FrameCount = 3;
ComPtr m_commandAllocators[FrameCount];
ComPtr m_commandList;
ComPtr m_renderTargets[FrameCount];
UINT m_frameIndex;
- // Synchronization
+ // Simple synchronization
ComPtr m_fence;
- UINT64 m_fenceValues[FrameCount];
+ UINT64 m_fenceValue; // Single incrementing counter
+ UINT64 m_frameCompletionValues[FrameCount]; // Per-frame completion tracking
HANDLE m_fenceEvent;
// YUV-to-RGB compute shader resources
@@ -61,16 +63,16 @@ private:
ComPtr m_srvUavHeap;
ComPtr m_computeShaderBlob;
- // Video textures
- ComPtr m_yTexture;
- ComPtr m_uTexture;
- ComPtr m_vTexture;
- ComPtr m_rgbTexture;
+ // Video textures - Triple buffered for proper synchronization
+ ComPtr m_yTextures[FrameCount];
+ ComPtr m_uTextures[FrameCount];
+ ComPtr m_vTextures[FrameCount];
+ ComPtr m_rgbTextures[FrameCount];
- // Upload resources for CPU->GPU transfer
- ComPtr m_yUploadBuffer;
- ComPtr m_uUploadBuffer;
- ComPtr m_vUploadBuffer;
+ // Upload resources for CPU->GPU transfer - Triple buffered
+ ComPtr m_yUploadBuffers[FrameCount];
+ ComPtr m_uUploadBuffers[FrameCount];
+ ComPtr m_vUploadBuffers[FrameCount];
// Constant buffer for shader
ComPtr m_constantBuffer;
@@ -93,10 +95,17 @@ private:
HRESULT CreateSynchronizationObjects();
HRESULT CreateComputeShaderResources();
HRESULT CreateVideoTextures(uint32_t videoWidth, uint32_t videoHeight);
- HRESULT UpdateVideoTextures(const VideoFrame& frame);
- HRESULT ExecuteComputeShader();
- HRESULT CopyToBackBuffer();
+ HRESULT ExecuteGPUPipeline(const VideoFrame& frame); // Consolidated GPU pipeline
+ HRESULT UpdateVideoTextures(const VideoFrame& frame); // Legacy public interface
+ HRESULT ExecuteComputeShader(); // Legacy public interface
+ HRESULT CopyToBackBuffer(); // Legacy public interface
+
+ // Internal methods (no command list management)
+ HRESULT UpdateVideoTexturesInternal(const VideoFrame& frame);
+ HRESULT ExecuteComputeShaderInternal();
+ HRESULT CopyToBackBufferInternal();
HRESULT WaitForGPU();
+ void WaitForFrameCompletion(UINT frameIndex); // Simple frame-specific wait
// Shader compilation
HRESULT CompileComputeShader();