Add renderer trace scope contract

This commit is contained in:
2026-06-02 16:55:23 +02:00
parent 07293c0590
commit bbe3db1747
9 changed files with 305 additions and 101 deletions

View File

@@ -575,13 +575,48 @@ RecordingRenderTrace::RecordingRenderTrace(std::vector<RecordedRenderCommand>& c
{
}
void RecordingRenderTrace::marker(const char* component, const char* name) noexcept
pp::foundation::Status RecordingRenderTrace::marker(const char* component, const char* name) noexcept
{
const auto status = validate_trace_label(component, name);
if (!status.ok()) {
return status;
}
push_command(commands_, RecordedRenderCommand {
.kind = RecordedRenderCommandKind::trace_marker,
.component = non_null_name(component),
.name = non_null_name(name),
});
return pp::foundation::Status::success();
}
pp::foundation::Status RecordingRenderTrace::begin_scope(const char* component, const char* name) noexcept
{
const auto status = validate_trace_label(component, name);
if (!status.ok()) {
return status;
}
push_command(commands_, RecordedRenderCommand {
.kind = RecordedRenderCommandKind::trace_begin_scope,
.component = non_null_name(component),
.name = non_null_name(name),
});
++scope_depth_;
return pp::foundation::Status::success();
}
pp::foundation::Status RecordingRenderTrace::end_scope() noexcept
{
if (scope_depth_ == 0U) {
return pp::foundation::Status::invalid_argument("trace scope has not begun");
}
push_command(commands_, RecordedRenderCommand {
.kind = RecordedRenderCommandKind::trace_end_scope,
});
--scope_depth_;
return pp::foundation::Status::success();
}
RecordingRenderDevice::RecordingRenderDevice() noexcept
@@ -731,6 +766,10 @@ const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) no
return "end_render_pass";
case RecordedRenderCommandKind::trace_marker:
return "trace_marker";
case RecordedRenderCommandKind::trace_begin_scope:
return "trace_begin_scope";
case RecordedRenderCommandKind::trace_end_scope:
return "trace_end_scope";
}
return "unknown";

View File

@@ -27,6 +27,8 @@ enum class RecordedRenderCommandKind : std::uint8_t {
blit_render_target,
end_render_pass,
trace_marker,
trace_begin_scope,
trace_end_scope,
};
struct RecordedRenderCommand {
@@ -176,10 +178,13 @@ private:
class RecordingRenderTrace final : public IRenderTrace {
public:
explicit RecordingRenderTrace(std::vector<RecordedRenderCommand>& commands) noexcept;
void marker(const char* component, const char* name) noexcept override;
[[nodiscard]] pp::foundation::Status marker(const char* component, const char* name) noexcept override;
[[nodiscard]] pp::foundation::Status begin_scope(const char* component, const char* name) noexcept override;
[[nodiscard]] pp::foundation::Status end_scope() noexcept override;
private:
std::vector<RecordedRenderCommand>* commands_ = nullptr;
std::uint32_t scope_depth_ = 0;
};
class RecordingRenderDevice final : public IRenderDevice {

View File

@@ -12,6 +12,20 @@ namespace {
return text == nullptr || text[0] == '\0';
}
[[nodiscard]] std::size_t bounded_c_string_length(const char* text, std::size_t limit) noexcept
{
if (text == nullptr) {
return 0;
}
std::size_t length = 0;
while (length <= limit && text[length] != '\0') {
++length;
}
return length;
}
[[nodiscard]] pp::foundation::Status validate_shader_stage_source(
ShaderStageSource source,
const char* stage_name) noexcept
@@ -506,6 +520,27 @@ pp::foundation::Status validate_shader_uniform_write(
return pp::foundation::Status::success();
}
pp::foundation::Status validate_trace_label(const char* component, const char* name) noexcept
{
if (is_empty_c_string(component)) {
return pp::foundation::Status::invalid_argument("trace component must not be empty");
}
if (is_empty_c_string(name)) {
return pp::foundation::Status::invalid_argument("trace name must not be empty");
}
if (bounded_c_string_length(component, max_trace_label_bytes) > max_trace_label_bytes) {
return pp::foundation::Status::out_of_range("trace component exceeds the configured limit");
}
if (bounded_c_string_length(name, max_trace_label_bytes) > max_trace_label_bytes) {
return pp::foundation::Status::out_of_range("trace name exceeds the configured limit");
}
return pp::foundation::Status::success();
}
pp::foundation::Status validate_readback_region(TextureDesc desc, ReadbackRegion region) noexcept
{
const auto extent_status = validate_extent(desc.extent);

View File

@@ -16,6 +16,7 @@ constexpr std::uint32_t max_texture_slots = 32;
constexpr std::uint64_t max_texture_bytes = 1024ULL * 1024ULL * 1024ULL;
constexpr std::size_t max_shader_source_bytes = 4ULL * 1024ULL * 1024ULL;
constexpr std::size_t max_shader_uniform_bytes = 64ULL * 1024ULL;
constexpr std::size_t max_trace_label_bytes = 256;
enum class TextureFormat : std::uint8_t {
rgba8,
@@ -244,7 +245,9 @@ public:
class IRenderTrace {
public:
virtual ~IRenderTrace() = default;
virtual void marker(const char* component, const char* name) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status marker(const char* component, const char* name) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status begin_scope(const char* component, const char* name) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status end_scope() noexcept = 0;
};
class ICommandContext {
@@ -338,6 +341,7 @@ public:
[[nodiscard]] pp::foundation::Status validate_shader_uniform_write(
const char* name,
std::span<const std::byte> bytes) noexcept;
[[nodiscard]] pp::foundation::Status validate_trace_label(const char* component, const char* name) 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,