Add renderer texture transition contract
This commit is contained in:
@@ -289,16 +289,16 @@ Known local toolchain state:
|
||||
render-pass color/depth/stencil clear intent, scissor state, depth state,
|
||||
blend state, texture-slot binding, sampler-state binding, texture-upload byte
|
||||
counts, texture mip-level counts, resource debug labels, mipmap-generation commands,
|
||||
shader-uniform writes, explicit draw descriptor ranges, texture-copy regions,
|
||||
texture-state transitions, shader-uniform writes, explicit draw descriptor ranges, texture-copy regions,
|
||||
readback bounds, frame-capture sources, 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-copy/readback/
|
||||
sampler-bind/draw/upload/mipmap-generation/texture-transition/texture-copy/readback/
|
||||
frame-capture/blit commands, draw mesh inputs, explicit draw ranges, and
|
||||
records trace markers and scopes without a window or GL context.
|
||||
- `pano_cli record-render` exposes the recording renderer through JSON
|
||||
automation, including render-pass/depth-clear counts, scissor/depth/blend/
|
||||
shader-uniform/texture-bind/sampler-bind/upload/mipmap-generation/texture-copy/readback/
|
||||
shader-uniform/texture-bind/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/
|
||||
frame-capture/blit command and byte totals, trace marker/scope counts,
|
||||
labeled descriptor counts, backend resource creation counts, plus draw
|
||||
descriptor vertex/index totals, and is covered by
|
||||
|
||||
@@ -419,7 +419,7 @@ mesh, readback bounds, command context, render device, shader program
|
||||
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, frame-capture byte-size helpers,
|
||||
mipmap-generation command validation, texture-state transition validation, frame-capture byte-size helpers,
|
||||
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,
|
||||
@@ -833,13 +833,13 @@ Results:
|
||||
renderer-owned resource factory and
|
||||
command-order/render-pass-clear/scissor-state/depth-state/blend-state/
|
||||
texture-usage/texture-bind/sampler-bind/shader-uniform/texture-upload/
|
||||
mipmap-generation/readback/frame-capture/blit validation plus explicit draw
|
||||
mipmap-generation/texture-transition/readback/frame-capture/blit validation plus explicit draw
|
||||
descriptor and texture-copy validation; it creates validated textures,
|
||||
render targets, shaders, meshes, and readback buffers with validated debug
|
||||
labels, then records commands, trace markers/scopes, render-pass
|
||||
color/depth/stencil clear intent, scissor state, depth state, blend state,
|
||||
shader uniform writes, texture/sampler binds, draw mesh inputs, explicit draw
|
||||
ranges, texture uploads/mipmap generations/copies/readbacks, frame captures,
|
||||
ranges, texture uploads/mipmap generations/state transitions/copies/readbacks, frame captures,
|
||||
and render-target blits, giving automation a backend-neutral render path that
|
||||
does not require a window or GL context.
|
||||
- `pano_cli record-render` exercises that headless recording renderer and emits
|
||||
@@ -847,7 +847,7 @@ Results:
|
||||
name, trace marker/scope and draw summary, labeled descriptor counts,
|
||||
render-pass/depth-clear counts, and draw
|
||||
descriptor vertex/index totals, scissor/depth/blend-state plus
|
||||
shader-uniform/texture/sampler-bind/upload/mipmap-generation/texture-copy/readback/
|
||||
shader-uniform/texture/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/
|
||||
frame-capture/blit command/byte totals for agent automation, with an
|
||||
expected-failure smoke for oversized render/readback targets.
|
||||
- `pano_cli simulate-document-history` exercises pure document history
|
||||
|
||||
@@ -435,6 +435,30 @@ pp::foundation::Status RecordingCommandContext::generate_mipmaps(ITexture2D& tex
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::transition_texture(
|
||||
ITexture2D& texture,
|
||||
TextureState before,
|
||||
TextureState after) noexcept
|
||||
{
|
||||
if (in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("texture transition must be outside a render pass");
|
||||
}
|
||||
|
||||
const auto desc = texture.desc();
|
||||
const auto desc_status = validate_texture_transition_desc(desc, before, after);
|
||||
if (!desc_status.ok()) {
|
||||
return desc_status;
|
||||
}
|
||||
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::transition_texture,
|
||||
.texture_desc = desc,
|
||||
.before_state = before,
|
||||
.after_state = after,
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::copy_texture(
|
||||
ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
@@ -754,6 +778,8 @@ const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) no
|
||||
return "upload_texture";
|
||||
case RecordedRenderCommandKind::generate_mipmaps:
|
||||
return "generate_mipmaps";
|
||||
case RecordedRenderCommandKind::transition_texture:
|
||||
return "transition_texture";
|
||||
case RecordedRenderCommandKind::copy_texture:
|
||||
return "copy_texture";
|
||||
case RecordedRenderCommandKind::read_texture:
|
||||
|
||||
@@ -21,6 +21,7 @@ enum class RecordedRenderCommandKind : std::uint8_t {
|
||||
draw,
|
||||
upload_texture,
|
||||
generate_mipmaps,
|
||||
transition_texture,
|
||||
copy_texture,
|
||||
read_texture,
|
||||
capture_frame,
|
||||
@@ -52,6 +53,8 @@ struct RecordedRenderCommand {
|
||||
std::uint32_t sampler_slot = 0;
|
||||
TextureDesc source_desc {};
|
||||
TextureDesc destination_desc {};
|
||||
TextureState before_state = TextureState::undefined;
|
||||
TextureState after_state = TextureState::undefined;
|
||||
ReadbackRegion readback_region {};
|
||||
ReadbackRegion source_region {};
|
||||
ReadbackRegion destination_region {};
|
||||
@@ -148,6 +151,10 @@ public:
|
||||
std::span<const std::byte> rgba_or_channel_bytes) noexcept override;
|
||||
[[nodiscard]] pp::foundation::Status generate_mipmaps(
|
||||
ITexture2D& texture) noexcept override;
|
||||
[[nodiscard]] pp::foundation::Status transition_texture(
|
||||
ITexture2D& texture,
|
||||
TextureState before,
|
||||
TextureState after) noexcept override;
|
||||
[[nodiscard]] pp::foundation::Status copy_texture(
|
||||
ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
|
||||
@@ -692,6 +692,78 @@ pp::foundation::Status validate_mipmap_generation_desc(TextureDesc desc) noexcep
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_texture_state(TextureState state) noexcept
|
||||
{
|
||||
switch (state) {
|
||||
case TextureState::undefined:
|
||||
case TextureState::shader_read:
|
||||
case TextureState::render_target:
|
||||
case TextureState::upload_destination:
|
||||
case TextureState::copy_source:
|
||||
case TextureState::copy_destination:
|
||||
case TextureState::readback_source:
|
||||
case TextureState::present:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("texture state is not supported");
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_texture_transition_desc(
|
||||
TextureDesc desc,
|
||||
TextureState before,
|
||||
TextureState after) noexcept
|
||||
{
|
||||
const auto desc_status = validate_texture_desc(desc);
|
||||
if (!desc_status.ok()) {
|
||||
return desc_status;
|
||||
}
|
||||
|
||||
const auto before_status = validate_texture_state(before);
|
||||
if (!before_status.ok()) {
|
||||
return before_status;
|
||||
}
|
||||
|
||||
const auto after_status = validate_texture_state(after);
|
||||
if (!after_status.ok()) {
|
||||
return after_status;
|
||||
}
|
||||
|
||||
if (before == after) {
|
||||
return pp::foundation::Status::invalid_argument("texture transition must change state");
|
||||
}
|
||||
|
||||
if (after == TextureState::undefined) {
|
||||
return pp::foundation::Status::invalid_argument("texture transition destination must not be undefined");
|
||||
}
|
||||
|
||||
if (after == TextureState::shader_read && !has_texture_usage(desc.usage, TextureUsage::sampled)) {
|
||||
return pp::foundation::Status::invalid_argument("shader-read transition requires sampled usage");
|
||||
}
|
||||
|
||||
if (after == TextureState::render_target && !has_texture_usage(desc.usage, TextureUsage::render_target)) {
|
||||
return pp::foundation::Status::invalid_argument("render-target transition requires render_target usage");
|
||||
}
|
||||
|
||||
if (after == TextureState::upload_destination && !has_texture_usage(desc.usage, TextureUsage::upload_destination)) {
|
||||
return pp::foundation::Status::invalid_argument("upload transition requires upload_destination usage");
|
||||
}
|
||||
|
||||
if (after == TextureState::copy_source && !has_texture_usage(desc.usage, TextureUsage::copy_source)) {
|
||||
return pp::foundation::Status::invalid_argument("copy-source transition requires copy_source usage");
|
||||
}
|
||||
|
||||
if (after == TextureState::copy_destination && !has_texture_usage(desc.usage, TextureUsage::copy_destination)) {
|
||||
return pp::foundation::Status::invalid_argument("copy-destination transition requires copy_destination usage");
|
||||
}
|
||||
|
||||
if (after == TextureState::readback_source && !has_texture_usage(desc.usage, TextureUsage::readback_source)) {
|
||||
return pp::foundation::Status::invalid_argument("readback transition requires readback_source usage");
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept
|
||||
{
|
||||
switch (filter) {
|
||||
@@ -749,6 +821,30 @@ const char* texture_format_name(TextureFormat format) noexcept
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char* texture_state_name(TextureState state) noexcept
|
||||
{
|
||||
switch (state) {
|
||||
case TextureState::undefined:
|
||||
return "undefined";
|
||||
case TextureState::shader_read:
|
||||
return "shader_read";
|
||||
case TextureState::render_target:
|
||||
return "render_target";
|
||||
case TextureState::upload_destination:
|
||||
return "upload_destination";
|
||||
case TextureState::copy_source:
|
||||
return "copy_source";
|
||||
case TextureState::copy_destination:
|
||||
return "copy_destination";
|
||||
case TextureState::readback_source:
|
||||
return "readback_source";
|
||||
case TextureState::present:
|
||||
return "present";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char* primitive_topology_name(PrimitiveTopology topology) noexcept
|
||||
{
|
||||
switch (topology) {
|
||||
|
||||
@@ -35,6 +35,17 @@ enum class TextureUsage : std::uint32_t {
|
||||
copy_destination = 1U << 5U,
|
||||
};
|
||||
|
||||
enum class TextureState : std::uint8_t {
|
||||
undefined,
|
||||
shader_read,
|
||||
render_target,
|
||||
upload_destination,
|
||||
copy_source,
|
||||
copy_destination,
|
||||
readback_source,
|
||||
present,
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr TextureUsage operator|(TextureUsage lhs, TextureUsage rhs) noexcept
|
||||
{
|
||||
return static_cast<TextureUsage>(
|
||||
@@ -285,6 +296,10 @@ public:
|
||||
std::span<const std::byte> rgba_or_channel_bytes) noexcept = 0;
|
||||
[[nodiscard]] virtual pp::foundation::Status generate_mipmaps(
|
||||
ITexture2D& texture) noexcept = 0;
|
||||
[[nodiscard]] virtual pp::foundation::Status transition_texture(
|
||||
ITexture2D& texture,
|
||||
TextureState before,
|
||||
TextureState after) noexcept = 0;
|
||||
[[nodiscard]] virtual pp::foundation::Status copy_texture(
|
||||
ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
@@ -358,11 +373,17 @@ public:
|
||||
TextureDesc destination,
|
||||
ReadbackRegion destination_region) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_mipmap_generation_desc(TextureDesc desc) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_texture_state(TextureState state) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_texture_transition_desc(
|
||||
TextureDesc desc,
|
||||
TextureState before,
|
||||
TextureState after) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_blit_descs(
|
||||
TextureDesc source,
|
||||
TextureDesc destination) noexcept;
|
||||
[[nodiscard]] const char* texture_format_name(TextureFormat format) noexcept;
|
||||
[[nodiscard]] const char* texture_state_name(TextureState state) noexcept;
|
||||
[[nodiscard]] const char* primitive_topology_name(PrimitiveTopology topology) noexcept;
|
||||
[[nodiscard]] const char* blit_filter_name(BlitFilter filter) noexcept;
|
||||
[[nodiscard]] const char* blend_factor_name(BlendFactor factor) noexcept;
|
||||
|
||||
@@ -365,7 +365,7 @@ if(TARGET pano_cli)
|
||||
COMMAND pano_cli record-render --width 32 --height 16)
|
||||
set_tests_properties(pano_cli_record_render_smoke PROPERTIES
|
||||
LABELS "renderer;integration;desktop-fast"
|
||||
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"createdResources\":7.*\"labeledCommandDescriptors\":12.*\"commands\":21.*\"renderPasses\":1.*\"depthClears\":1.*\"stencilClears\":0.*\"drawCommands\":1.*\"drawVertices\":3.*\"drawIndices\":3.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"uniformCommands\":1.*\"uniformBytes\":64.*\"bindTextureCommands\":1.*\"bindSamplerCommands\":1.*\"boundTextureBytes\":2048.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"mipmapCommands\":1.*\"mipmapLevels\":3.*\"mipmapBytes\":84.*\"copyCommands\":1.*\"copySourceBytes\":2048.*\"copyDestinationBytes\":2048.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048.*\"traceMarkers\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1")
|
||||
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"createdResources\":7.*\"labeledCommandDescriptors\":16.*\"commands\":25.*\"renderPasses\":1.*\"depthClears\":1.*\"stencilClears\":0.*\"drawCommands\":1.*\"drawVertices\":3.*\"drawIndices\":3.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"uniformCommands\":1.*\"uniformBytes\":64.*\"bindTextureCommands\":1.*\"bindSamplerCommands\":1.*\"boundTextureBytes\":2048.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"mipmapCommands\":1.*\"mipmapLevels\":3.*\"mipmapBytes\":84.*\"transitionCommands\":4.*\"transitionToUpload\":1.*\"transitionToShaderRead\":2.*\"copyCommands\":1.*\"copySourceBytes\":2048.*\"copyDestinationBytes\":2048.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048.*\"traceMarkers\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1")
|
||||
|
||||
add_test(NAME pano_cli_record_render_rejects_oversized_target
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
|
||||
@@ -54,6 +54,7 @@ using pp::renderer::ShaderProgramDesc;
|
||||
using pp::renderer::ShaderStageSource;
|
||||
using pp::renderer::TextureDesc;
|
||||
using pp::renderer::TextureFormat;
|
||||
using pp::renderer::TextureState;
|
||||
using pp::renderer::TextureUsage;
|
||||
using pp::renderer::Viewport;
|
||||
using pp::renderer::max_shader_source_bytes;
|
||||
@@ -70,6 +71,7 @@ using pp::renderer::recorded_render_command_kind_name;
|
||||
using pp::renderer::ShaderCatalogEntry;
|
||||
using pp::renderer::texture_byte_size;
|
||||
using pp::renderer::texture_format_name;
|
||||
using pp::renderer::texture_state_name;
|
||||
using pp::renderer::validate_extent;
|
||||
using pp::renderer::validate_blit_descs;
|
||||
using pp::renderer::validate_blit_filter;
|
||||
@@ -94,6 +96,8 @@ using pp::renderer::validate_shader_uniform_write;
|
||||
using pp::renderer::validate_texture_copy_descs;
|
||||
using pp::renderer::validate_texture_desc;
|
||||
using pp::renderer::validate_texture_slot;
|
||||
using pp::renderer::validate_texture_state;
|
||||
using pp::renderer::validate_texture_transition_desc;
|
||||
using pp::renderer::validate_texture_usage;
|
||||
using pp::renderer::validate_trace_label;
|
||||
using pp::renderer::validate_viewport;
|
||||
@@ -457,6 +461,21 @@ public:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status transition_texture(
|
||||
pp::renderer::ITexture2D& texture,
|
||||
TextureState before,
|
||||
TextureState after) noexcept override
|
||||
{
|
||||
const auto status = validate_texture_transition_desc(texture.desc(), before, after);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
last_transition_before = before;
|
||||
last_transition_after = after;
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status copy_texture(
|
||||
pp::renderer::ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
@@ -559,6 +578,8 @@ public:
|
||||
std::uint64_t last_upload_bytes = 0;
|
||||
std::uint32_t last_generated_mip_levels = 0;
|
||||
std::uint64_t last_generated_mip_bytes = 0;
|
||||
TextureState last_transition_before = TextureState::undefined;
|
||||
TextureState last_transition_after = TextureState::undefined;
|
||||
std::uint64_t last_copy_source_bytes = 0;
|
||||
std::uint64_t last_copy_destination_bytes = 0;
|
||||
std::uint64_t last_readback_bytes = 0;
|
||||
@@ -852,6 +873,133 @@ void validates_mipmap_generation_contract(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, missing_copy_destination.code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void validates_texture_transition_contract(pp::tests::Harness& h)
|
||||
{
|
||||
const TextureDesc full_usage_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_sampled_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::upload_destination | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_render_target_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_upload_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_copy_source_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_copy_destination_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source,
|
||||
};
|
||||
const TextureDesc missing_readback_desc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
|
||||
PP_EXPECT(h, validate_texture_state(TextureState::present).ok());
|
||||
PP_EXPECT(h, validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::undefined,
|
||||
TextureState::upload_destination)
|
||||
.ok());
|
||||
PP_EXPECT(h, validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::upload_destination,
|
||||
TextureState::shader_read)
|
||||
.ok());
|
||||
PP_EXPECT(h, validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::shader_read,
|
||||
TextureState::render_target)
|
||||
.ok());
|
||||
PP_EXPECT(h, validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::copy_destination,
|
||||
TextureState::readback_source)
|
||||
.ok());
|
||||
PP_EXPECT(h, validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::render_target,
|
||||
TextureState::present)
|
||||
.ok());
|
||||
PP_EXPECT(h, texture_state_name(TextureState::copy_destination) == std::string_view("copy_destination"));
|
||||
|
||||
const auto invalid_state = validate_texture_state(static_cast<TextureState>(255));
|
||||
const auto same_state = validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::shader_read,
|
||||
TextureState::shader_read);
|
||||
const auto undefined_destination = validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
TextureState::shader_read,
|
||||
TextureState::undefined);
|
||||
const auto invalid_before = validate_texture_transition_desc(
|
||||
full_usage_desc,
|
||||
static_cast<TextureState>(255),
|
||||
TextureState::shader_read);
|
||||
const auto missing_sampled = validate_texture_transition_desc(
|
||||
missing_sampled_desc,
|
||||
TextureState::copy_destination,
|
||||
TextureState::shader_read);
|
||||
const auto missing_render_target = validate_texture_transition_desc(
|
||||
missing_render_target_desc,
|
||||
TextureState::shader_read,
|
||||
TextureState::render_target);
|
||||
const auto missing_upload = validate_texture_transition_desc(
|
||||
missing_upload_desc,
|
||||
TextureState::undefined,
|
||||
TextureState::upload_destination);
|
||||
const auto missing_copy_source = validate_texture_transition_desc(
|
||||
missing_copy_source_desc,
|
||||
TextureState::shader_read,
|
||||
TextureState::copy_source);
|
||||
const auto missing_copy_destination = validate_texture_transition_desc(
|
||||
missing_copy_destination_desc,
|
||||
TextureState::shader_read,
|
||||
TextureState::copy_destination);
|
||||
const auto missing_readback = validate_texture_transition_desc(
|
||||
missing_readback_desc,
|
||||
TextureState::copy_source,
|
||||
TextureState::readback_source);
|
||||
|
||||
PP_EXPECT(h, !invalid_state.ok());
|
||||
PP_EXPECT(h, invalid_state.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !same_state.ok());
|
||||
PP_EXPECT(h, same_state.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !undefined_destination.ok());
|
||||
PP_EXPECT(h, undefined_destination.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !invalid_before.ok());
|
||||
PP_EXPECT(h, invalid_before.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_sampled.ok());
|
||||
PP_EXPECT(h, missing_sampled.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_render_target.ok());
|
||||
PP_EXPECT(h, missing_render_target.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_upload.ok());
|
||||
PP_EXPECT(h, missing_upload.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_copy_source.ok());
|
||||
PP_EXPECT(h, missing_copy_source.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_copy_destination.ok());
|
||||
PP_EXPECT(h, missing_copy_destination.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_readback.ok());
|
||||
PP_EXPECT(h, missing_readback.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, texture_state_name(static_cast<TextureState>(255)) == std::string_view("unknown"));
|
||||
}
|
||||
|
||||
void rejects_invalid_or_excessive_extents(pp::tests::Harness& h)
|
||||
{
|
||||
const auto zero = validate_extent(Extent2D { .width = 0, .height = 1 });
|
||||
@@ -1455,6 +1603,11 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h)
|
||||
ReadbackRegion { .x = 2, .y = 3, .width = 4, .height = 5 },
|
||||
upload_bytes)
|
||||
.ok());
|
||||
PP_EXPECT(h, context.transition_texture(
|
||||
texture,
|
||||
TextureState::upload_destination,
|
||||
TextureState::shader_read)
|
||||
.ok());
|
||||
PP_EXPECT(h, context.copy_texture(
|
||||
texture,
|
||||
ReadbackRegion { .x = 2, .y = 3, .width = 4, .height = 5 },
|
||||
@@ -1497,6 +1650,8 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, device.context.last_sampler_desc.mag_filter == SamplerFilter::nearest);
|
||||
PP_EXPECT(h, device.context.last_sampler_desc.address_u == SamplerAddressMode::repeat);
|
||||
PP_EXPECT(h, device.context.last_upload_bytes == 80U);
|
||||
PP_EXPECT(h, device.context.last_transition_before == TextureState::upload_destination);
|
||||
PP_EXPECT(h, device.context.last_transition_after == TextureState::shader_read);
|
||||
PP_EXPECT(h, device.context.last_copy_source_bytes == 80U);
|
||||
PP_EXPECT(h, device.context.last_copy_destination_bytes == 80U);
|
||||
PP_EXPECT(h, device.context.last_readback_bytes == 80U);
|
||||
@@ -1665,6 +1820,72 @@ void recording_renderer_records_mipmap_generation(pp::tests::Harness& h)
|
||||
context.end_render_pass();
|
||||
}
|
||||
|
||||
void recording_renderer_records_texture_transitions(pp::tests::Harness& h)
|
||||
{
|
||||
RecordingRenderDevice device;
|
||||
RecordingTexture2D texture(TextureDesc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::render_target | TextureUsage::sampled | TextureUsage::upload_destination | TextureUsage::readback_source | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
.debug_name = "transition-texture",
|
||||
});
|
||||
RecordingTexture2D texture_without_sampled(TextureDesc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = TextureUsage::upload_destination | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
});
|
||||
RecordingRenderTarget target(TextureDesc {
|
||||
.extent = Extent2D { .width = 8, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = all_texture_usages,
|
||||
});
|
||||
|
||||
auto& context = device.immediate_context();
|
||||
PP_EXPECT(h, context.transition_texture(
|
||||
texture,
|
||||
TextureState::undefined,
|
||||
TextureState::upload_destination)
|
||||
.ok());
|
||||
PP_EXPECT(h, context.transition_texture(
|
||||
texture,
|
||||
TextureState::upload_destination,
|
||||
TextureState::shader_read)
|
||||
.ok());
|
||||
|
||||
const auto commands = device.commands();
|
||||
PP_EXPECT(h, commands.size() == 2U);
|
||||
PP_EXPECT(h, commands[0].kind == RecordedRenderCommandKind::transition_texture);
|
||||
PP_EXPECT(h, commands[0].texture_desc.debug_name == std::string_view("transition-texture"));
|
||||
PP_EXPECT(h, commands[0].before_state == TextureState::undefined);
|
||||
PP_EXPECT(h, commands[0].after_state == TextureState::upload_destination);
|
||||
PP_EXPECT(h, commands[1].before_state == TextureState::upload_destination);
|
||||
PP_EXPECT(h, commands[1].after_state == TextureState::shader_read);
|
||||
PP_EXPECT(h, recorded_render_command_kind_name(commands[0].kind) == std::string_view("transition_texture"));
|
||||
|
||||
const auto no_op = context.transition_texture(
|
||||
texture,
|
||||
TextureState::shader_read,
|
||||
TextureState::shader_read);
|
||||
PP_EXPECT(h, !no_op.ok());
|
||||
PP_EXPECT(h, no_op.code == StatusCode::invalid_argument);
|
||||
|
||||
const auto missing_usage = context.transition_texture(
|
||||
texture_without_sampled,
|
||||
TextureState::copy_destination,
|
||||
TextureState::shader_read);
|
||||
PP_EXPECT(h, !missing_usage.ok());
|
||||
PP_EXPECT(h, missing_usage.code == StatusCode::invalid_argument);
|
||||
|
||||
PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {}).ok());
|
||||
const auto during_render_pass = context.transition_texture(
|
||||
texture,
|
||||
TextureState::shader_read,
|
||||
TextureState::render_target);
|
||||
PP_EXPECT(h, !during_render_pass.ok());
|
||||
PP_EXPECT(h, during_render_pass.code == StatusCode::invalid_argument);
|
||||
context.end_render_pass();
|
||||
}
|
||||
|
||||
void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
|
||||
{
|
||||
RecordingRenderDevice device;
|
||||
@@ -2290,6 +2511,7 @@ int main()
|
||||
harness.run("validates_texture_usage_contract", validates_texture_usage_contract);
|
||||
harness.run("validates_resource_labels", validates_resource_labels);
|
||||
harness.run("validates_mipmap_generation_contract", validates_mipmap_generation_contract);
|
||||
harness.run("validates_texture_transition_contract", validates_texture_transition_contract);
|
||||
harness.run("rejects_invalid_or_excessive_extents", rejects_invalid_or_excessive_extents);
|
||||
harness.run("validates_readback_bounds", validates_readback_bounds);
|
||||
harness.run("computes_readback_byte_sizes", computes_readback_byte_sizes);
|
||||
@@ -2311,6 +2533,7 @@ int main()
|
||||
harness.run("render_devices_create_validated_resources", render_devices_create_validated_resources);
|
||||
harness.run("recording_renderer_records_shader_uniform_writes", recording_renderer_records_shader_uniform_writes);
|
||||
harness.run("recording_renderer_records_mipmap_generation", recording_renderer_records_mipmap_generation);
|
||||
harness.run("recording_renderer_records_texture_transitions", recording_renderer_records_texture_transitions);
|
||||
harness.run("recording_renderer_records_valid_command_sequences", recording_renderer_records_valid_command_sequences);
|
||||
harness.run("recording_renderer_rejects_invalid_command_order_and_targets", recording_renderer_rejects_invalid_command_order_and_targets);
|
||||
return harness.finish();
|
||||
|
||||
@@ -2313,6 +2313,15 @@ int record_render(int argc, char** argv)
|
||||
}
|
||||
|
||||
auto& context = device.immediate_context();
|
||||
const auto transition_upload_status = context.transition_texture(
|
||||
*texture.value(),
|
||||
pp::renderer::TextureState::undefined,
|
||||
pp::renderer::TextureState::upload_destination);
|
||||
if (!transition_upload_status.ok()) {
|
||||
print_error("record-render", transition_upload_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto upload_status = context.upload_texture(
|
||||
*texture.value(),
|
||||
pp::renderer::ReadbackRegion {
|
||||
@@ -2327,12 +2336,39 @@ int record_render(int argc, char** argv)
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto transition_shader_read_status = context.transition_texture(
|
||||
*texture.value(),
|
||||
pp::renderer::TextureState::upload_destination,
|
||||
pp::renderer::TextureState::shader_read);
|
||||
if (!transition_shader_read_status.ok()) {
|
||||
print_error("record-render", transition_shader_read_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto transition_mip_destination_status = context.transition_texture(
|
||||
*mip_texture.value(),
|
||||
pp::renderer::TextureState::undefined,
|
||||
pp::renderer::TextureState::copy_destination);
|
||||
if (!transition_mip_destination_status.ok()) {
|
||||
print_error("record-render", transition_mip_destination_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto mipmap_status = context.generate_mipmaps(*mip_texture.value());
|
||||
if (!mipmap_status.ok()) {
|
||||
print_error("record-render", mipmap_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto transition_mip_shader_read_status = context.transition_texture(
|
||||
*mip_texture.value(),
|
||||
pp::renderer::TextureState::copy_destination,
|
||||
pp::renderer::TextureState::shader_read);
|
||||
if (!transition_mip_shader_read_status.ok()) {
|
||||
print_error("record-render", transition_mip_shader_read_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto begin_status = context.begin_render_pass(
|
||||
*target.value(),
|
||||
pp::renderer::RenderPassDesc {
|
||||
@@ -2497,6 +2533,9 @@ int record_render(int argc, char** argv)
|
||||
std::size_t bind_sampler_commands = 0;
|
||||
std::size_t upload_commands = 0;
|
||||
std::size_t mipmap_commands = 0;
|
||||
std::size_t transition_commands = 0;
|
||||
std::size_t transition_to_upload = 0;
|
||||
std::size_t transition_to_shader_read = 0;
|
||||
std::size_t copy_commands = 0;
|
||||
std::size_t readback_commands = 0;
|
||||
std::size_t capture_commands = 0;
|
||||
@@ -2567,6 +2606,15 @@ int record_render(int argc, char** argv)
|
||||
count_label(command.texture_desc.debug_name);
|
||||
mipmap_levels += command.generated_mip_levels;
|
||||
mipmap_bytes += command.generated_mip_bytes;
|
||||
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::transition_texture) {
|
||||
++transition_commands;
|
||||
count_label(command.texture_desc.debug_name);
|
||||
if (command.after_state == pp::renderer::TextureState::upload_destination) {
|
||||
++transition_to_upload;
|
||||
}
|
||||
if (command.after_state == pp::renderer::TextureState::shader_read) {
|
||||
++transition_to_shader_read;
|
||||
}
|
||||
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::upload_texture) {
|
||||
++upload_commands;
|
||||
count_label(command.texture_desc.debug_name);
|
||||
@@ -2627,6 +2675,9 @@ int record_render(int argc, char** argv)
|
||||
<< ",\"mipmapCommands\":" << mipmap_commands
|
||||
<< ",\"mipmapLevels\":" << mipmap_levels
|
||||
<< ",\"mipmapBytes\":" << mipmap_bytes
|
||||
<< ",\"transitionCommands\":" << transition_commands
|
||||
<< ",\"transitionToUpload\":" << transition_to_upload
|
||||
<< ",\"transitionToShaderRead\":" << transition_to_shader_read
|
||||
<< ",\"copyCommands\":" << copy_commands
|
||||
<< ",\"copySourceBytes\":" << copy_source_bytes
|
||||
<< ",\"copyDestinationBytes\":" << copy_destination_bytes
|
||||
|
||||
Reference in New Issue
Block a user