diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp index 0edf2bbee17..832549a021b 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp @@ -2402,6 +2402,55 @@ d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc return true; } +// Helper function to calculate the max output bitstream size based on width, height, and format +// This function uses an approach based on common video formats and their typical compression ratios +inline +UINT d3d12_video_encoder_calculate_max_output_compressed_bitstream_size( + UINT uiWidth, + UINT uiHeight, + DXGI_FORMAT format +) +{ + assert((uiHeight > 16) && + (uiWidth > 16) && + (format != DXGI_FORMAT_UNKNOWN)); + + const UINT MIN_BUFFER_SIZE = 128 * 128 * 2; // Minimum buffer size for very small frames: 128x128 pixels at 2 bytes/pixel + const UINT MAX_BUFFER_SIZE = 20 * 1024 * 1024; // Maximum buffer size of 20MB + const float EXPECTED_COMPRESSION_FACTOR = 2.0f; // Assume 50% of calculated size after compression of raw pixel sizes + + UINT alignedWidth = (uiWidth + 15) & ~15; + UINT alignedHeight = (uiHeight + 15) & ~15; + UINT bufferSize = 0; + switch (format) { + case DXGI_FORMAT_NV12: + // NV12: Y plane (1 byte/pixel) + UV plane (1/2 byte/pixel) = 1.5 bytes/pixel + bufferSize = alignedWidth * alignedHeight * 3 / 2; + break; + case DXGI_FORMAT_P010: + // P010: Y plane (2 bytes/pixel) + UV plane (1 byte/pixel) = 3 bytes/pixel + bufferSize = alignedWidth * alignedHeight * 3; + break; + case DXGI_FORMAT_AYUV: + // AYUV: 4 bytes/pixel + bufferSize = alignedWidth * alignedHeight * 4; + break; + default: + // Fallback formula for other formats: assume 15 bits/pixel (1.875 bytes/pixel) + bufferSize = (((alignedHeight) * (alignedWidth) * 15) >> 3); + break; + } + + // Apply EXPECTED_COMPRESSION_FACTOR constant (% of calculated size) + bufferSize = static_cast(std::ceil(bufferSize / EXPECTED_COMPRESSION_FACTOR)); + + // Clamp buffer size between minimum and maximum limits + bufferSize = std::max(MIN_BUFFER_SIZE, std::min(bufferSize, MAX_BUFFER_SIZE)); + + return bufferSize; +} + + struct pipe_video_codec * d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *codec) { @@ -2445,6 +2494,20 @@ d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pi goto failed; } + pD3D12Enc->m_MaxOutputBitstreamSize = d3d12_video_encoder_calculate_max_output_compressed_bitstream_size( + codec->width, + codec->height, + d3d12_convert_pipe_video_profile_to_dxgi_format(pD3D12Enc->base.profile) + ); + + debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_encoder - Calculated max output bitstream size: %u bytes (%u Kb, %u Mb) for %ux%u DXGI_FORMAT %u\n", + pD3D12Enc->m_MaxOutputBitstreamSize, + pD3D12Enc->m_MaxOutputBitstreamSize / 1024, + pD3D12Enc->m_MaxOutputBitstreamSize / (1024 * 1024), + codec->width, + codec->height, + static_cast(d3d12_convert_pipe_video_profile_to_dxgi_format(pD3D12Enc->base.profile))); + if (!d3d12_video_encoder_create_command_objects(pD3D12Enc)) { debug_printf("[d3d12_video_encoder] d3d12_video_encoder_create_encoder - Failure on " "d3d12_video_encoder_create_command_objects\n"); @@ -3225,7 +3288,7 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec, pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstreams[0/*first slice*/]; } else if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstreams[slice_idx] == nullptr) { D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); - CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_DEFAULT_COMPBIT_STAGING_SIZE); + CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(pD3D12Enc->m_MaxOutputBitstreamSize); HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource( &Properties, D3D12_HEAP_FLAG_NONE, @@ -4356,7 +4419,7 @@ d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, templ.target = PIPE_BUFFER; templ.usage = PIPE_USAGE_DEFAULT; templ.format = PIPE_FORMAT_R8_UINT; - templ.width0 = D3D12_DEFAULT_COMPBIT_STAGING_SIZE; + templ.width0 = pD3D12Enc->m_MaxOutputBitstreamSize; templ.height0 = 1; templ.depth0 = 1; templ.array_size = 1; diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.h b/src/gallium/drivers/d3d12/d3d12_video_enc.h index 39c3d662180..8bae36df1b8 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.h +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.h @@ -574,6 +574,7 @@ struct d3d12_video_encoder const uint m_NodeMask = 0u; const uint m_NodeIndex = 0u; + UINT m_MaxOutputBitstreamSize = 0; ComPtr m_spFence; uint64_t m_fenceValue = 1u; diff --git a/src/gallium/drivers/d3d12/d3d12_video_types.h b/src/gallium/drivers/d3d12/d3d12_video_types.h index 095ae319aa1..108055453ff 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_types.h +++ b/src/gallium/drivers/d3d12/d3d12_video_types.h @@ -75,8 +75,6 @@ const size_t D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT = static_cast(debug_ constexpr unsigned int D3D12_VIDEO_H264_MB_IN_PIXELS = 16; -constexpr size_t D3D12_DEFAULT_COMPBIT_STAGING_SIZE = (1024 /*1K*/ * 1024/*1MB*/) * 8/*8 MB*/; // 8MB - /* If enabled, the D3D12 AV1 encoder will use always ...CONFIGURABLE_GRID_PARTITION mode */ /* If disabled, the D3D12 AV1 encoder will try to use ...UNIFORM_GRID_PARTITION first and then fallback to ...CONFIGURABLE_GRID_PARTITION if not possible */ const bool D3D12_VIDEO_FORCE_TILE_MODE = debug_get_bool_option("D3D12_VIDEO_FORCE_TILE_MODE", false);