Add renderer mipmap command contract

This commit is contained in:
2026-06-02 16:47:44 +02:00
parent 901aff1051
commit 07293c0590
7 changed files with 279 additions and 29 deletions

View File

@@ -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) {