From 0fc73d51d201e50cdd48217099b7c9d1b898e447 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 2 Jun 2026 18:06:23 +0200 Subject: [PATCH] Validate renderer blit descriptors first --- docs/modernization/build-inventory.md | 2 +- docs/modernization/roadmap.md | 2 +- src/renderer_api/renderer_api.cpp | 27 ++++++++++++++----- tests/renderer_api/renderer_api_tests.cpp | 32 +++++++++++++++++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 912184a..46f3988 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -303,7 +303,7 @@ Known local toolchain state: blend state, texture-slot binding, sampler-state binding, texture-upload byte counts, texture mip-level counts, texture/mesh/shader resource debug labels, mipmap-generation commands, texture-state transitions, shader-uniform writes, explicit draw descriptor ranges, texture-copy regions, - readback descriptor/bounds, frame-capture sources, destination buffer sizes, and + readback/frame-capture/blit descriptor validation, readback bounds, destination buffer sizes, and render-target blit regions, records render-pass-clear/scissor/depth/blend/shader-uniform/texture-bind/ sampler-bind/draw/upload/mipmap-generation/texture-transition/texture-copy/readback/ diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 5810215..ec0c28b 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -428,7 +428,7 @@ descriptor, mesh, render target, readback byte-size helpers, texture mip-level validation, resource debug-label validation, texture-upload/readback command validation, mipmap-generation command validation, texture-state transition validation, frame-capture byte-size helpers, -readback/copy descriptor validation, frame-capture command validation, render-target blit validation, texture-slot +readback/copy/frame-capture/blit descriptor validation, frame-capture command validation, render-target blit validation, texture-slot binding validation, blend-state validation, scissor-state validation, depth-state validation, trace marker/scope validation, sampler-state validation, texture/mesh/shader resource-label validation, diff --git a/src/renderer_api/renderer_api.cpp b/src/renderer_api/renderer_api.cpp index 2b1831b..ef86ac5 100644 --- a/src/renderer_api/renderer_api.cpp +++ b/src/renderer_api/renderer_api.cpp @@ -624,6 +624,11 @@ pp::foundation::Result readback_byte_size(TextureDesc desc, Readb pp::foundation::Result frame_capture_byte_size(TextureDesc desc) noexcept { + const auto desc_status = validate_texture_desc(desc); + if (!desc_status.ok()) { + return pp::foundation::Result::failure(desc_status); + } + if (!has_texture_usage(desc.usage, TextureUsage::render_target)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("frame capture source must be a render target")); @@ -778,6 +783,16 @@ pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept pp::foundation::Status validate_blit_descs(TextureDesc source, TextureDesc destination) noexcept { + const auto source_status = validate_texture_desc(source); + if (!source_status.ok()) { + return source_status; + } + + const auto destination_status = validate_texture_desc(destination); + if (!destination_status.ok()) { + return destination_status; + } + if (!has_texture_usage(source.usage, TextureUsage::render_target) || !has_texture_usage(destination.usage, TextureUsage::render_target)) { return pp::foundation::Status::invalid_argument("blit endpoints must be render targets"); @@ -795,14 +810,14 @@ pp::foundation::Status validate_blit_descs(TextureDesc source, TextureDesc desti return pp::foundation::Status::invalid_argument("blit endpoints must use matching texture formats"); } - const auto source_status = texture_byte_size(source); - if (!source_status.ok()) { - return source_status.status(); + const auto source_bytes = texture_byte_size(source); + if (!source_bytes.ok()) { + return source_bytes.status(); } - const auto destination_status = texture_byte_size(destination); - if (!destination_status.ok()) { - return destination_status.status(); + const auto destination_bytes = texture_byte_size(destination); + if (!destination_bytes.ok()) { + return destination_bytes.status(); } return pp::foundation::Status::success(); diff --git a/tests/renderer_api/renderer_api_tests.cpp b/tests/renderer_api/renderer_api_tests.cpp index 01e52ad..97645a2 100644 --- a/tests/renderer_api/renderer_api_tests.cpp +++ b/tests/renderer_api/renderer_api_tests.cpp @@ -1132,14 +1132,30 @@ void computes_frame_capture_byte_sizes(pp::tests::Harness& h) .format = TextureFormat::rgba8, .usage = TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination, }; + const TextureDesc unsupported_usage_desc { + .extent = Extent2D { .width = 16, .height = 8 }, + .format = TextureFormat::rgba8, + .usage = TextureUsage::render_target | TextureUsage::readback_source | static_cast(1U << 31U), + }; + const TextureDesc unknown_format_desc { + .extent = Extent2D { .width = 16, .height = 8 }, + .format = static_cast(255), + .usage = TextureUsage::render_target | TextureUsage::readback_source, + }; const auto capture = frame_capture_byte_size(target_desc); const auto non_target = frame_capture_byte_size(texture_desc); + const auto unsupported_usage = frame_capture_byte_size(unsupported_usage_desc); + const auto unknown_format = frame_capture_byte_size(unknown_format_desc); PP_EXPECT(h, capture.ok()); PP_EXPECT(h, capture.value() == 512U); PP_EXPECT(h, !non_target.ok()); PP_EXPECT(h, non_target.status().code == StatusCode::invalid_argument); + PP_EXPECT(h, !unsupported_usage.ok()); + PP_EXPECT(h, unsupported_usage.status().code == StatusCode::invalid_argument); + PP_EXPECT(h, !unknown_format.ok()); + PP_EXPECT(h, unknown_format.status().code == StatusCode::invalid_argument); } void validates_blit_contract(pp::tests::Harness& h) @@ -1159,6 +1175,16 @@ void validates_blit_contract(pp::tests::Harness& h) .format = TextureFormat::rgba8, .usage = TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination, }; + const TextureDesc unknown_format_target_desc { + .extent = Extent2D { .width = 16, .height = 8 }, + .format = static_cast(255), + .usage = TextureUsage::render_target | TextureUsage::copy_source | TextureUsage::copy_destination, + }; + const TextureDesc unsupported_usage_target_desc { + .extent = Extent2D { .width = 16, .height = 8 }, + .format = TextureFormat::rgba8, + .usage = TextureUsage::render_target | TextureUsage::copy_source | TextureUsage::copy_destination | static_cast(1U << 31U), + }; PP_EXPECT(h, validate_blit_descs(target_desc, target_desc).ok()); PP_EXPECT(h, validate_blit_filter(BlitFilter::nearest).ok()); @@ -1167,12 +1193,18 @@ void validates_blit_contract(pp::tests::Harness& h) const auto non_target = validate_blit_descs(texture_desc, target_desc); const auto mismatched_format = validate_blit_descs(target_desc, r8_target_desc); + const auto unknown_format = validate_blit_descs(unknown_format_target_desc, unknown_format_target_desc); + const auto unsupported_usage = validate_blit_descs(unsupported_usage_target_desc, target_desc); const auto bad_filter = validate_blit_filter(static_cast(255)); PP_EXPECT(h, !non_target.ok()); PP_EXPECT(h, non_target.code == StatusCode::invalid_argument); PP_EXPECT(h, !mismatched_format.ok()); PP_EXPECT(h, mismatched_format.code == StatusCode::invalid_argument); + PP_EXPECT(h, !unknown_format.ok()); + PP_EXPECT(h, unknown_format.code == StatusCode::invalid_argument); + PP_EXPECT(h, !unsupported_usage.ok()); + PP_EXPECT(h, unsupported_usage.code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_filter.ok()); PP_EXPECT(h, bad_filter.code == StatusCode::invalid_argument); PP_EXPECT(h, blit_filter_name(static_cast(255)) == std::string_view("unknown"));