Add renderer mipmap command contract
This commit is contained in:
@@ -58,6 +58,7 @@ using pp::renderer::TextureUsage;
|
||||
using pp::renderer::Viewport;
|
||||
using pp::renderer::max_shader_source_bytes;
|
||||
using pp::renderer::max_shader_uniform_bytes;
|
||||
using pp::renderer::max_mip_levels_for_extent;
|
||||
using pp::renderer::max_texture_dimension;
|
||||
using pp::renderer::max_texture_slots;
|
||||
using pp::renderer::panopainter_shader_catalog;
|
||||
@@ -77,6 +78,7 @@ using pp::renderer::validate_compare_op;
|
||||
using pp::renderer::validate_depth_state;
|
||||
using pp::renderer::validate_draw_desc;
|
||||
using pp::renderer::validate_mesh_desc;
|
||||
using pp::renderer::validate_mipmap_generation_desc;
|
||||
using pp::renderer::validate_readback_region;
|
||||
using pp::renderer::validate_render_pass_desc;
|
||||
using pp::renderer::validate_sampler_address_mode;
|
||||
@@ -397,6 +399,24 @@ public:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status generate_mipmaps(
|
||||
pp::renderer::ITexture2D& texture) noexcept override
|
||||
{
|
||||
const auto status = validate_mipmap_generation_desc(texture.desc());
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
const auto bytes = texture_byte_size(texture.desc());
|
||||
if (!bytes.ok()) {
|
||||
return bytes.status();
|
||||
}
|
||||
|
||||
last_generated_mip_levels = texture.desc().mip_levels;
|
||||
last_generated_mip_bytes = bytes.value();
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status copy_texture(
|
||||
pp::renderer::ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
@@ -497,6 +517,8 @@ public:
|
||||
std::uint32_t last_sampler_slot = 0;
|
||||
SamplerDesc last_sampler_desc {};
|
||||
std::uint64_t last_upload_bytes = 0;
|
||||
std::uint32_t last_generated_mip_levels = 0;
|
||||
std::uint64_t last_generated_mip_bytes = 0;
|
||||
std::uint64_t last_copy_source_bytes = 0;
|
||||
std::uint64_t last_copy_destination_bytes = 0;
|
||||
std::uint64_t last_readback_bytes = 0;
|
||||
@@ -629,6 +651,14 @@ void computes_texture_sizes(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, r8.ok());
|
||||
PP_EXPECT(h, r8.value() == 128U);
|
||||
PP_EXPECT(h, texture_format_name(TextureFormat::depth24_stencil8) == std::string_view("depth24_stencil8"));
|
||||
|
||||
const auto mipmapped = texture_byte_size(TextureDesc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 3,
|
||||
});
|
||||
PP_EXPECT(h, mipmapped.ok());
|
||||
PP_EXPECT(h, mipmapped.value() == 84U);
|
||||
}
|
||||
|
||||
void validates_texture_usage_contract(pp::tests::Harness& h)
|
||||
@@ -676,6 +706,68 @@ void validates_texture_usage_contract(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, unknown_format.code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void validates_mipmap_generation_contract(pp::tests::Harness& h)
|
||||
{
|
||||
const TextureDesc mipmapped_desc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 3,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc one_mip_desc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 1,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc too_many_mips_desc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 4,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_sampled_desc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 3,
|
||||
.usage = TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_copy_source_desc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 3,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_destination,
|
||||
};
|
||||
const TextureDesc missing_copy_destination_desc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 3,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source,
|
||||
};
|
||||
|
||||
PP_EXPECT(h, max_mip_levels_for_extent(Extent2D { .width = 1, .height = 1 }) == 1U);
|
||||
PP_EXPECT(h, max_mip_levels_for_extent(Extent2D { .width = 4, .height = 3 }) == 3U);
|
||||
PP_EXPECT(h, max_mip_levels_for_extent(Extent2D { .width = max_texture_dimension, .height = 1 }) == 16U);
|
||||
PP_EXPECT(h, validate_mipmap_generation_desc(mipmapped_desc).ok());
|
||||
|
||||
const auto one_mip = validate_mipmap_generation_desc(one_mip_desc);
|
||||
const auto too_many_mips = validate_mipmap_generation_desc(too_many_mips_desc);
|
||||
const auto missing_sampled = validate_mipmap_generation_desc(missing_sampled_desc);
|
||||
const auto missing_copy_source = validate_mipmap_generation_desc(missing_copy_source_desc);
|
||||
const auto missing_copy_destination = validate_mipmap_generation_desc(missing_copy_destination_desc);
|
||||
|
||||
PP_EXPECT(h, !one_mip.ok());
|
||||
PP_EXPECT(h, one_mip.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !too_many_mips.ok());
|
||||
PP_EXPECT(h, too_many_mips.code == StatusCode::out_of_range);
|
||||
PP_EXPECT(h, !missing_sampled.ok());
|
||||
PP_EXPECT(h, missing_sampled.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);
|
||||
}
|
||||
|
||||
void rejects_invalid_or_excessive_extents(pp::tests::Harness& h)
|
||||
{
|
||||
const auto zero = validate_extent(Extent2D { .width = 0, .height = 1 });
|
||||
@@ -1410,6 +1502,48 @@ void recording_renderer_records_shader_uniform_writes(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, commands[3].kind == RecordedRenderCommandKind::end_render_pass);
|
||||
}
|
||||
|
||||
void recording_renderer_records_mipmap_generation(pp::tests::Harness& h)
|
||||
{
|
||||
RecordingRenderDevice device;
|
||||
RecordingTexture2D texture(TextureDesc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 3,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
});
|
||||
RecordingTexture2D one_mip_texture(TextureDesc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.mip_levels = 1,
|
||||
.usage = TextureUsage::sampled | TextureUsage::copy_source | TextureUsage::copy_destination,
|
||||
});
|
||||
RecordingRenderTarget target(TextureDesc {
|
||||
.extent = Extent2D { .width = 4, .height = 4 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.usage = all_texture_usages,
|
||||
});
|
||||
|
||||
auto& context = device.immediate_context();
|
||||
PP_EXPECT(h, context.generate_mipmaps(texture).ok());
|
||||
const auto commands = device.commands();
|
||||
PP_EXPECT(h, commands.size() == 1U);
|
||||
PP_EXPECT(h, commands[0].kind == RecordedRenderCommandKind::generate_mipmaps);
|
||||
PP_EXPECT(h, commands[0].texture_desc.mip_levels == 3U);
|
||||
PP_EXPECT(h, commands[0].generated_mip_levels == 3U);
|
||||
PP_EXPECT(h, commands[0].generated_mip_bytes == 84U);
|
||||
PP_EXPECT(h, recorded_render_command_kind_name(commands[0].kind) == std::string_view("generate_mipmaps"));
|
||||
|
||||
const auto one_mip = context.generate_mipmaps(one_mip_texture);
|
||||
PP_EXPECT(h, !one_mip.ok());
|
||||
PP_EXPECT(h, one_mip.code == StatusCode::invalid_argument);
|
||||
|
||||
PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {}).ok());
|
||||
const auto during_render_pass = context.generate_mipmaps(texture);
|
||||
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;
|
||||
@@ -1997,6 +2131,7 @@ int main()
|
||||
pp::tests::Harness harness;
|
||||
harness.run("computes_texture_sizes", computes_texture_sizes);
|
||||
harness.run("validates_texture_usage_contract", validates_texture_usage_contract);
|
||||
harness.run("validates_mipmap_generation_contract", validates_mipmap_generation_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);
|
||||
@@ -2016,6 +2151,7 @@ int main()
|
||||
harness.run("renderer_interfaces_support_backend_neutral_dispatch", renderer_interfaces_support_backend_neutral_dispatch);
|
||||
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_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