Add renderer mipmap command contract
This commit is contained in:
@@ -409,6 +409,32 @@ pp::foundation::Status RecordingCommandContext::upload_texture(
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::generate_mipmaps(ITexture2D& texture) noexcept
|
||||
{
|
||||
if (in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("mipmap generation must be outside a render pass");
|
||||
}
|
||||
|
||||
const auto desc = texture.desc();
|
||||
const auto desc_status = validate_mipmap_generation_desc(desc);
|
||||
if (!desc_status.ok()) {
|
||||
return desc_status;
|
||||
}
|
||||
|
||||
const auto bytes = texture_byte_size(desc);
|
||||
if (!bytes.ok()) {
|
||||
return bytes.status();
|
||||
}
|
||||
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::generate_mipmaps,
|
||||
.texture_desc = desc,
|
||||
.generated_mip_levels = desc.mip_levels,
|
||||
.generated_mip_bytes = bytes.value(),
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::copy_texture(
|
||||
ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
@@ -691,6 +717,8 @@ const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) no
|
||||
return "draw";
|
||||
case RecordedRenderCommandKind::upload_texture:
|
||||
return "upload_texture";
|
||||
case RecordedRenderCommandKind::generate_mipmaps:
|
||||
return "generate_mipmaps";
|
||||
case RecordedRenderCommandKind::copy_texture:
|
||||
return "copy_texture";
|
||||
case RecordedRenderCommandKind::read_texture:
|
||||
|
||||
@@ -20,6 +20,7 @@ enum class RecordedRenderCommandKind : std::uint8_t {
|
||||
bind_mesh,
|
||||
draw,
|
||||
upload_texture,
|
||||
generate_mipmaps,
|
||||
copy_texture,
|
||||
read_texture,
|
||||
capture_frame,
|
||||
@@ -54,6 +55,8 @@ struct RecordedRenderCommand {
|
||||
ReadbackRegion destination_region {};
|
||||
BlitFilter blit_filter = BlitFilter::nearest;
|
||||
std::uint64_t upload_bytes = 0;
|
||||
std::uint32_t generated_mip_levels = 0;
|
||||
std::uint64_t generated_mip_bytes = 0;
|
||||
std::uint64_t copy_source_bytes = 0;
|
||||
std::uint64_t copy_destination_bytes = 0;
|
||||
std::uint64_t readback_bytes = 0;
|
||||
@@ -141,6 +144,8 @@ public:
|
||||
ITexture2D& texture,
|
||||
ReadbackRegion region,
|
||||
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 copy_texture(
|
||||
ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
|
||||
@@ -31,6 +31,18 @@ namespace {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] Extent2D mip_level_extent(Extent2D extent, std::uint32_t level) noexcept
|
||||
{
|
||||
auto width = extent.width;
|
||||
auto height = extent.height;
|
||||
for (std::uint32_t index = 0; index < level; ++index) {
|
||||
width = width > 1U ? width / 2U : 1U;
|
||||
height = height > 1U ? height / 2U : 1U;
|
||||
}
|
||||
|
||||
return Extent2D { .width = width, .height = height };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::uint32_t bytes_per_pixel(TextureFormat format) noexcept
|
||||
@@ -47,6 +59,22 @@ std::uint32_t bytes_per_pixel(TextureFormat format) noexcept
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t max_mip_levels_for_extent(Extent2D extent) noexcept
|
||||
{
|
||||
if (extent.width == 0U || extent.height == 0U) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto dimension = extent.width > extent.height ? extent.width : extent.height;
|
||||
std::uint32_t levels = 1;
|
||||
while (dimension > 1U && levels < max_texture_mip_levels) {
|
||||
dimension /= 2U;
|
||||
++levels;
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
bool has_texture_usage(TextureUsage usage, TextureUsage required) noexcept
|
||||
{
|
||||
const auto usage_bits = static_cast<std::uint32_t>(usage);
|
||||
@@ -100,6 +128,14 @@ pp::foundation::Status validate_texture_desc(TextureDesc desc) noexcept
|
||||
return pp::foundation::Status::invalid_argument("texture format is not supported");
|
||||
}
|
||||
|
||||
if (desc.mip_levels == 0U) {
|
||||
return pp::foundation::Status::invalid_argument("texture mip level count must be greater than zero");
|
||||
}
|
||||
|
||||
if (desc.mip_levels > max_mip_levels_for_extent(desc.extent)) {
|
||||
return pp::foundation::Status::out_of_range("texture mip level count exceeds the texture extent");
|
||||
}
|
||||
|
||||
return validate_texture_usage(desc.usage);
|
||||
}
|
||||
|
||||
@@ -111,20 +147,30 @@ pp::foundation::Result<std::uint64_t> texture_byte_size(TextureDesc desc) noexce
|
||||
}
|
||||
|
||||
const auto bpp = static_cast<std::uint64_t>(bytes_per_pixel(desc.format));
|
||||
const auto width = static_cast<std::uint64_t>(desc.extent.width);
|
||||
const auto height = static_cast<std::uint64_t>(desc.extent.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("texture size overflows uint64"));
|
||||
std::uint64_t bytes = 0;
|
||||
for (std::uint32_t level = 0; level < desc.mip_levels; ++level) {
|
||||
const auto level_extent = mip_level_extent(desc.extent, level);
|
||||
const auto width = static_cast<std::uint64_t>(level_extent.width);
|
||||
const auto height = static_cast<std::uint64_t>(level_extent.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("texture size 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("texture byte size overflows uint64"));
|
||||
}
|
||||
|
||||
const auto level_bytes = pixels * bpp;
|
||||
if (bytes > std::numeric_limits<std::uint64_t>::max() - level_bytes) {
|
||||
return pp::foundation::Result<std::uint64_t>::failure(
|
||||
pp::foundation::Status::out_of_range("texture byte size overflows uint64"));
|
||||
}
|
||||
bytes += level_bytes;
|
||||
}
|
||||
|
||||
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("texture 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("texture byte size exceeds the configured limit"));
|
||||
@@ -562,6 +608,32 @@ pp::foundation::Status validate_texture_copy_descs(
|
||||
return validate_readback_region(destination, destination_region);
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_mipmap_generation_desc(TextureDesc desc) noexcept
|
||||
{
|
||||
const auto desc_status = validate_texture_desc(desc);
|
||||
if (!desc_status.ok()) {
|
||||
return desc_status;
|
||||
}
|
||||
|
||||
if (desc.mip_levels <= 1U) {
|
||||
return pp::foundation::Status::invalid_argument("mipmap generation requires more than one mip level");
|
||||
}
|
||||
|
||||
if (!has_texture_usage(desc.usage, TextureUsage::sampled)) {
|
||||
return pp::foundation::Status::invalid_argument("mipmap texture must allow sampled usage");
|
||||
}
|
||||
|
||||
if (!has_texture_usage(desc.usage, TextureUsage::copy_source)) {
|
||||
return pp::foundation::Status::invalid_argument("mipmap texture must allow copy_source usage");
|
||||
}
|
||||
|
||||
if (!has_texture_usage(desc.usage, TextureUsage::copy_destination)) {
|
||||
return pp::foundation::Status::invalid_argument("mipmap texture must allow copy_destination usage");
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept
|
||||
{
|
||||
switch (filter) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace pp::renderer {
|
||||
|
||||
constexpr std::uint32_t max_texture_dimension = 32768;
|
||||
constexpr std::uint32_t max_texture_mip_levels = 16;
|
||||
constexpr std::uint32_t max_mesh_vertices = 16777216;
|
||||
constexpr std::uint32_t max_texture_slots = 32;
|
||||
constexpr std::uint64_t max_texture_bytes = 1024ULL * 1024ULL * 1024ULL;
|
||||
@@ -58,6 +59,7 @@ struct Extent2D {
|
||||
struct TextureDesc {
|
||||
Extent2D extent;
|
||||
TextureFormat format = TextureFormat::rgba8;
|
||||
std::uint32_t mip_levels = 1;
|
||||
TextureUsage usage = TextureUsage::sampled
|
||||
| TextureUsage::upload_destination
|
||||
| TextureUsage::readback_source
|
||||
@@ -275,6 +277,8 @@ public:
|
||||
ITexture2D& texture,
|
||||
ReadbackRegion region,
|
||||
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 copy_texture(
|
||||
ITexture2D& source,
|
||||
ReadbackRegion source_region,
|
||||
@@ -311,6 +315,7 @@ public:
|
||||
};
|
||||
|
||||
[[nodiscard]] std::uint32_t bytes_per_pixel(TextureFormat format) noexcept;
|
||||
[[nodiscard]] std::uint32_t max_mip_levels_for_extent(Extent2D extent) noexcept;
|
||||
[[nodiscard]] bool has_texture_usage(TextureUsage usage, TextureUsage required) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_extent(Extent2D extent) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_texture_usage(TextureUsage usage) noexcept;
|
||||
@@ -344,6 +349,7 @@ public:
|
||||
ReadbackRegion source_region,
|
||||
TextureDesc destination,
|
||||
ReadbackRegion destination_region) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_mipmap_generation_desc(TextureDesc desc) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_blit_descs(
|
||||
TextureDesc source,
|
||||
|
||||
Reference in New Issue
Block a user