Add renderer texture transition contract
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user