Add renderer readback command contract

This commit is contained in:
2026-06-02 15:10:44 +02:00
parent a6a4e7b249
commit c58b9a3718
9 changed files with 255 additions and 11 deletions

View File

@@ -174,6 +174,34 @@ pp::foundation::Status RecordingCommandContext::draw() noexcept
return pp::foundation::Status::success();
}
pp::foundation::Status RecordingCommandContext::read_texture(
ITexture2D& texture,
ReadbackRegion region,
IReadbackBuffer& destination) noexcept
{
if (in_render_pass_) {
return pp::foundation::Status::invalid_argument("readback must be outside a render pass");
}
const auto desc = texture.desc();
const auto bytes = readback_byte_size(desc, region);
if (!bytes) {
return bytes.status();
}
if (destination.size_bytes() < bytes.value()) {
return pp::foundation::Status::out_of_range("readback buffer is too small");
}
push_command(commands_, RecordedRenderCommand {
.kind = RecordedRenderCommandKind::read_texture,
.texture_desc = desc,
.readback_region = region,
.readback_bytes = bytes.value(),
});
return pp::foundation::Status::success();
}
void RecordingCommandContext::end_render_pass() noexcept
{
if (!in_render_pass_) {
@@ -251,6 +279,8 @@ const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) no
return "bind_mesh";
case RecordedRenderCommandKind::draw:
return "draw";
case RecordedRenderCommandKind::read_texture:
return "read_texture";
case RecordedRenderCommandKind::end_render_pass:
return "end_render_pass";
case RecordedRenderCommandKind::trace_marker:

View File

@@ -13,6 +13,7 @@ enum class RecordedRenderCommandKind : std::uint8_t {
bind_shader,
bind_mesh,
draw,
read_texture,
end_render_pass,
trace_marker,
};
@@ -23,6 +24,9 @@ struct RecordedRenderCommand {
ClearColor clear_color {};
Viewport viewport {};
MeshDesc mesh_desc {};
TextureDesc texture_desc {};
ReadbackRegion readback_region {};
std::uint64_t readback_bytes = 0;
const char* component = "";
const char* name = "";
};
@@ -83,6 +87,10 @@ public:
[[nodiscard]] pp::foundation::Status bind_shader(IShaderProgram& shader) noexcept override;
[[nodiscard]] pp::foundation::Status bind_mesh(IMesh& mesh) noexcept override;
[[nodiscard]] pp::foundation::Status draw() noexcept override;
[[nodiscard]] pp::foundation::Status read_texture(
ITexture2D& texture,
ReadbackRegion region,
IReadbackBuffer& destination) noexcept override;
void end_render_pass() noexcept override;
[[nodiscard]] bool in_render_pass() const noexcept;

View File

@@ -196,6 +196,41 @@ pp::foundation::Status validate_readback_region(TextureDesc desc, ReadbackRegion
return pp::foundation::Status::success();
}
pp::foundation::Result<std::uint64_t> readback_byte_size(TextureDesc desc, ReadbackRegion region) noexcept
{
const auto region_status = validate_readback_region(desc, region);
if (!region_status.ok()) {
return pp::foundation::Result<std::uint64_t>::failure(region_status);
}
const auto bpp = static_cast<std::uint64_t>(bytes_per_pixel(desc.format));
if (bpp == 0) {
return pp::foundation::Result<std::uint64_t>::failure(
pp::foundation::Status::invalid_argument("texture format is not supported"));
}
const auto width = static_cast<std::uint64_t>(region.width);
const auto height = static_cast<std::uint64_t>(region.height);
if (width > std::numeric_limits<std::uint64_t>::max() / height) {
return pp::foundation::Result<std::uint64_t>::failure(
pp::foundation::Status::out_of_range("readback pixel count overflows uint64"));
}
const auto pixels = width * height;
if (pixels > std::numeric_limits<std::uint64_t>::max() / bpp) {
return pp::foundation::Result<std::uint64_t>::failure(
pp::foundation::Status::out_of_range("readback byte size overflows uint64"));
}
const auto bytes = pixels * bpp;
if (bytes > max_texture_bytes) {
return pp::foundation::Result<std::uint64_t>::failure(
pp::foundation::Status::out_of_range("readback byte size exceeds the configured limit"));
}
return pp::foundation::Result<std::uint64_t>::success(bytes);
}
const char* texture_format_name(TextureFormat format) noexcept
{
switch (format) {

View File

@@ -122,6 +122,10 @@ public:
[[nodiscard]] virtual pp::foundation::Status bind_shader(IShaderProgram& shader) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status bind_mesh(IMesh& mesh) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status draw() noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status read_texture(
ITexture2D& texture,
ReadbackRegion region,
IReadbackBuffer& destination) noexcept = 0;
virtual void end_render_pass() noexcept = 0;
};
@@ -139,6 +143,9 @@ public:
[[nodiscard]] pp::foundation::Status validate_mesh_desc(MeshDesc desc) noexcept;
[[nodiscard]] pp::foundation::Status validate_shader_program_desc(ShaderProgramDesc desc) noexcept;
[[nodiscard]] pp::foundation::Result<std::uint64_t> texture_byte_size(TextureDesc desc) noexcept;
[[nodiscard]] pp::foundation::Result<std::uint64_t> readback_byte_size(
TextureDesc desc,
ReadbackRegion region) noexcept;
[[nodiscard]] pp::foundation::Status validate_readback_region(TextureDesc desc, ReadbackRegion region) noexcept;
[[nodiscard]] const char* texture_format_name(TextureFormat format) noexcept;
[[nodiscard]] const char* primitive_topology_name(PrimitiveTopology topology) noexcept;