Add renderer API validation tests
This commit is contained in:
@@ -102,6 +102,18 @@ target_link_libraries(pp_document
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
pp_project_warnings)
|
pp_project_warnings)
|
||||||
|
|
||||||
|
add_library(pp_renderer_api STATIC
|
||||||
|
src/renderer_api/renderer_api.cpp)
|
||||||
|
target_include_directories(pp_renderer_api
|
||||||
|
PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||||
|
target_link_libraries(pp_renderer_api
|
||||||
|
PUBLIC
|
||||||
|
pp_foundation
|
||||||
|
pp_project_options
|
||||||
|
PRIVATE
|
||||||
|
pp_project_warnings)
|
||||||
|
|
||||||
if(PP_BUILD_TOOLS)
|
if(PP_BUILD_TOOLS)
|
||||||
add_subdirectory(tools/pano_cli)
|
add_subdirectory(tools/pano_cli)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ Known local toolchain state:
|
|||||||
- Android NDK: `C:\Users\omara\AppData\Local\Android\Sdk\ndk\29.0.14206865`
|
- Android NDK: `C:\Users\omara\AppData\Local\Android\Sdk\ndk\29.0.14206865`
|
||||||
- Android arm64 headless configure/build passes through root CMake and the
|
- Android arm64 headless configure/build passes through root CMake and the
|
||||||
`platform-build` automation wrapper for `pp_foundation`, `pp_assets`,
|
`platform-build` automation wrapper for `pp_foundation`, `pp_assets`,
|
||||||
`pp_paint`, `pp_document`, `pano_cli`, and their current headless test
|
`pp_paint`, `pp_document`, `pp_renderer_api`, `pano_cli`, and their current
|
||||||
binaries.
|
headless test binaries.
|
||||||
- `vcpkg` is not on PATH yet; see DEBT-0007.
|
- `vcpkg` is not on PATH yet; see DEBT-0007.
|
||||||
|
|
||||||
Known warnings after the current CMake app build:
|
Known warnings after the current CMake app build:
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ or temporary adapters live only in chat history.
|
|||||||
| 2 | Toolchain, Diagnostics, And Dependencies | In progress | Strict desktop library builds compile cleanly |
|
| 2 | Toolchain, Diagnostics, And Dependencies | In progress | Strict desktop library builds compile cleanly |
|
||||||
| 3 | Test Harness And Agent-Ready Automation | In progress | `ctest --preset desktop-fast` runs headlessly |
|
| 3 | Test Harness And Agent-Ready Automation | In progress | `ctest --preset desktop-fast` runs headlessly |
|
||||||
| 4 | Component Split Without Behavior Change | Started | Each extracted target builds and tests |
|
| 4 | Component Split Without Behavior Change | Started | Each extracted target builds and tests |
|
||||||
| 5 | Renderer Boundary And OpenGL Parity | Not started | OpenGL output matches golden readbacks |
|
| 5 | Renderer Boundary And OpenGL Parity | Started | OpenGL output matches golden readbacks |
|
||||||
| 6 | Platform Alignment | Not started | Every supported platform has named validation |
|
| 6 | Platform Alignment | Not started | Every supported platform has named validation |
|
||||||
| 7 | Hardening, Coverage, And Breaking-Point Tests | Not started | Each component has edge/failure tests |
|
| 7 | Hardening, Coverage, And Breaking-Point Tests | Not started | Each component has edge/failure tests |
|
||||||
| 8 | Future Backend Readiness | Not started | Vulkan/Metal lab targets remain non-default |
|
| 8 | Future Backend Readiness | Not started | Vulkan/Metal lab targets remain non-default |
|
||||||
@@ -306,8 +306,10 @@ and stroke timing spans with invalid-end tests. `pp_assets` has started with
|
|||||||
PNG/JPEG signature detection and corrupt/truncated/unsupported tests.
|
PNG/JPEG signature detection and corrupt/truncated/unsupported tests.
|
||||||
`pp_paint` has started with CPU reference math for the five current shader
|
`pp_paint` has started with CPU reference math for the five current shader
|
||||||
blend modes. `pp_document` has started with a pure canvas/layer model and
|
blend modes. `pp_document` has started with a pure canvas/layer model and
|
||||||
layer invariant tests. Continue expanding document behavior toward legacy
|
layer invariant tests. `pp_renderer_api` has started with renderer-neutral
|
||||||
Canvas parity.
|
texture/readback descriptors and validation tests. Continue expanding document
|
||||||
|
behavior toward legacy Canvas parity and then port OpenGL classes behind the
|
||||||
|
renderer boundary.
|
||||||
|
|
||||||
Implementation tasks:
|
Implementation tasks:
|
||||||
|
|
||||||
@@ -343,6 +345,10 @@ Gate:
|
|||||||
Goal: make OpenGL an implementation detail and establish parity tests before
|
Goal: make OpenGL an implementation detail and establish parity tests before
|
||||||
adding new backends.
|
adding new backends.
|
||||||
|
|
||||||
|
Status: started. `pp_renderer_api` exists as a headless renderer-neutral target
|
||||||
|
with texture descriptor, byte-size, and readback bounds validation. OpenGL
|
||||||
|
classes are not yet behind these interfaces.
|
||||||
|
|
||||||
Implementation tasks:
|
Implementation tasks:
|
||||||
|
|
||||||
- Introduce renderer interfaces:
|
- Introduce renderer interfaces:
|
||||||
@@ -507,7 +513,7 @@ Last verified on 2026-05-31:
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cmake --preset windows-msvc-default
|
cmake --preset windows-msvc-default
|
||||||
cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pano_cli PanoPainter
|
cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pano_cli PanoPainter
|
||||||
ctest --preset desktop-fast --build-config Debug
|
ctest --preset desktop-fast --build-config Debug
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug
|
powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli
|
powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli
|
||||||
@@ -523,6 +529,7 @@ Results:
|
|||||||
- `pp_assets_image_format_tests` passed.
|
- `pp_assets_image_format_tests` passed.
|
||||||
- `pp_paint_blend_tests` passed.
|
- `pp_paint_blend_tests` passed.
|
||||||
- `pp_document_tests` passed.
|
- `pp_document_tests` passed.
|
||||||
|
- `pp_renderer_api_tests` passed.
|
||||||
- `pano_cli_create_document_smoke` passed.
|
- `pano_cli_create_document_smoke` passed.
|
||||||
- `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure
|
- `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure
|
||||||
test.
|
test.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[string[]]$Presets = @("android-arm64"),
|
[string[]]$Presets = @("android-arm64"),
|
||||||
[string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_parse_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_paint_blend_tests", "pp_document_tests")
|
[string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_parse_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_paint_blend_tests", "pp_document_tests", "pp_renderer_api_tests")
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ set -u
|
|||||||
|
|
||||||
preset="${1:-android-arm64}"
|
preset="${1:-android-arm64}"
|
||||||
shift || true
|
shift || true
|
||||||
targets="${*:-pp_foundation pp_assets pp_paint pp_document pano_cli pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests}"
|
targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pano_cli pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests}"
|
||||||
start="$(date +%s)"
|
start="$(date +%s)"
|
||||||
|
|
||||||
cmake --preset "$preset"
|
cmake --preset "$preset"
|
||||||
|
|||||||
105
src/renderer_api/renderer_api.cpp
Normal file
105
src/renderer_api/renderer_api.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include "renderer_api/renderer_api.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace pp::renderer {
|
||||||
|
|
||||||
|
std::uint32_t bytes_per_pixel(TextureFormat format) noexcept
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case TextureFormat::rgba8:
|
||||||
|
return 4;
|
||||||
|
case TextureFormat::r8:
|
||||||
|
return 1;
|
||||||
|
case TextureFormat::depth24_stencil8:
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status validate_extent(Extent2D extent) noexcept
|
||||||
|
{
|
||||||
|
if (extent.width == 0 || extent.height == 0) {
|
||||||
|
return pp::foundation::Status::invalid_argument("texture extent must be greater than zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extent.width > max_texture_dimension || extent.height > max_texture_dimension) {
|
||||||
|
return pp::foundation::Status::out_of_range("texture extent exceeds the configured limit");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<std::uint64_t> texture_byte_size(TextureDesc desc) noexcept
|
||||||
|
{
|
||||||
|
const auto extent_status = validate_extent(desc.extent);
|
||||||
|
if (!extent_status.ok()) {
|
||||||
|
return pp::foundation::Result<std::uint64_t>::failure(extent_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bpp = static_cast<std::uint64_t>(bytes_per_pixel(desc.format));
|
||||||
|
if (bpp == 0) {
|
||||||
|
return pp::foundation::Result<std::uint64_t>::failure(
|
||||||
|
pp::foundation::Status::invalid_argument("texture format is not supported"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Result<std::uint64_t>::success(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status validate_readback_region(TextureDesc desc, ReadbackRegion region) noexcept
|
||||||
|
{
|
||||||
|
const auto extent_status = validate_extent(desc.extent);
|
||||||
|
if (!extent_status.ok()) {
|
||||||
|
return extent_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.width == 0 || region.height == 0) {
|
||||||
|
return pp::foundation::Status::invalid_argument("readback region must be greater than zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.x > desc.extent.width || region.y > desc.extent.height) {
|
||||||
|
return pp::foundation::Status::out_of_range("readback origin is outside the texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.width > desc.extent.width - region.x || region.height > desc.extent.height - region.y) {
|
||||||
|
return pp::foundation::Status::out_of_range("readback region exceeds texture bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* texture_format_name(TextureFormat format) noexcept
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case TextureFormat::rgba8:
|
||||||
|
return "rgba8";
|
||||||
|
case TextureFormat::r8:
|
||||||
|
return "r8";
|
||||||
|
case TextureFormat::depth24_stencil8:
|
||||||
|
return "depth24_stencil8";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
src/renderer_api/renderer_api.h
Normal file
60
src/renderer_api/renderer_api.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "foundation/result.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace pp::renderer {
|
||||||
|
|
||||||
|
constexpr std::uint32_t max_texture_dimension = 32768;
|
||||||
|
constexpr std::uint64_t max_texture_bytes = 1024ULL * 1024ULL * 1024ULL;
|
||||||
|
|
||||||
|
enum class TextureFormat : std::uint8_t {
|
||||||
|
rgba8,
|
||||||
|
r8,
|
||||||
|
depth24_stencil8,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Extent2D {
|
||||||
|
std::uint32_t width = 0;
|
||||||
|
std::uint32_t height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextureDesc {
|
||||||
|
Extent2D extent;
|
||||||
|
TextureFormat format = TextureFormat::rgba8;
|
||||||
|
bool render_target = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReadbackRegion {
|
||||||
|
std::uint32_t x = 0;
|
||||||
|
std::uint32_t y = 0;
|
||||||
|
std::uint32_t width = 0;
|
||||||
|
std::uint32_t height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ITexture2D {
|
||||||
|
public:
|
||||||
|
virtual ~ITexture2D() = default;
|
||||||
|
[[nodiscard]] virtual TextureDesc desc() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IReadbackBuffer {
|
||||||
|
public:
|
||||||
|
virtual ~IReadbackBuffer() = default;
|
||||||
|
[[nodiscard]] virtual std::uint64_t size_bytes() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IRenderTrace {
|
||||||
|
public:
|
||||||
|
virtual ~IRenderTrace() = default;
|
||||||
|
virtual void marker(const char* component, const char* name) noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint32_t bytes_per_pixel(TextureFormat format) noexcept;
|
||||||
|
[[nodiscard]] pp::foundation::Status validate_extent(Extent2D extent) noexcept;
|
||||||
|
[[nodiscard]] pp::foundation::Result<std::uint64_t> texture_byte_size(TextureDesc desc) noexcept;
|
||||||
|
[[nodiscard]] pp::foundation::Status validate_readback_region(TextureDesc desc, ReadbackRegion region) noexcept;
|
||||||
|
[[nodiscard]] const char* texture_format_name(TextureFormat format) noexcept;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -66,6 +66,16 @@ add_test(NAME pp_document_tests COMMAND pp_document_tests)
|
|||||||
set_tests_properties(pp_document_tests PROPERTIES
|
set_tests_properties(pp_document_tests PROPERTIES
|
||||||
LABELS "document;desktop-fast")
|
LABELS "document;desktop-fast")
|
||||||
|
|
||||||
|
add_executable(pp_renderer_api_tests
|
||||||
|
renderer_api/renderer_api_tests.cpp)
|
||||||
|
target_link_libraries(pp_renderer_api_tests PRIVATE
|
||||||
|
pp_renderer_api
|
||||||
|
pp_test_harness)
|
||||||
|
|
||||||
|
add_test(NAME pp_renderer_api_tests COMMAND pp_renderer_api_tests)
|
||||||
|
set_tests_properties(pp_renderer_api_tests PROPERTIES
|
||||||
|
LABELS "renderer;desktop-fast")
|
||||||
|
|
||||||
if(TARGET pano_cli)
|
if(TARGET pano_cli)
|
||||||
add_test(NAME pano_cli_create_document_smoke
|
add_test(NAME pano_cli_create_document_smoke
|
||||||
COMMAND pano_cli create-document --width 64 --height 32 --layers 2)
|
COMMAND pano_cli create-document --width 64 --height 32 --layers 2)
|
||||||
|
|||||||
86
tests/renderer_api/renderer_api_tests.cpp
Normal file
86
tests/renderer_api/renderer_api_tests.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include "renderer_api/renderer_api.h"
|
||||||
|
#include "test_harness.h"
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
using pp::foundation::StatusCode;
|
||||||
|
using pp::renderer::Extent2D;
|
||||||
|
using pp::renderer::ReadbackRegion;
|
||||||
|
using pp::renderer::TextureDesc;
|
||||||
|
using pp::renderer::TextureFormat;
|
||||||
|
using pp::renderer::max_texture_dimension;
|
||||||
|
using pp::renderer::texture_byte_size;
|
||||||
|
using pp::renderer::texture_format_name;
|
||||||
|
using pp::renderer::validate_extent;
|
||||||
|
using pp::renderer::validate_readback_region;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void computes_texture_sizes(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto rgba = texture_byte_size(TextureDesc {
|
||||||
|
.extent = Extent2D { .width = 16, .height = 8 },
|
||||||
|
.format = TextureFormat::rgba8,
|
||||||
|
});
|
||||||
|
const auto r8 = texture_byte_size(TextureDesc {
|
||||||
|
.extent = Extent2D { .width = 16, .height = 8 },
|
||||||
|
.format = TextureFormat::r8,
|
||||||
|
});
|
||||||
|
|
||||||
|
PP_EXPECT(h, rgba.ok());
|
||||||
|
PP_EXPECT(h, rgba.value() == 512U);
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void rejects_invalid_or_excessive_extents(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto zero = validate_extent(Extent2D { .width = 0, .height = 1 });
|
||||||
|
const auto huge = validate_extent(Extent2D { .width = max_texture_dimension + 1U, .height = 1 });
|
||||||
|
const auto excessive_bytes = texture_byte_size(TextureDesc {
|
||||||
|
.extent = Extent2D { .width = max_texture_dimension, .height = max_texture_dimension },
|
||||||
|
.format = TextureFormat::rgba8,
|
||||||
|
});
|
||||||
|
|
||||||
|
PP_EXPECT(h, !zero.ok());
|
||||||
|
PP_EXPECT(h, zero.code == StatusCode::invalid_argument);
|
||||||
|
PP_EXPECT(h, !huge.ok());
|
||||||
|
PP_EXPECT(h, huge.code == StatusCode::out_of_range);
|
||||||
|
PP_EXPECT(h, !excessive_bytes.ok());
|
||||||
|
PP_EXPECT(h, excessive_bytes.status().code == StatusCode::out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void validates_readback_bounds(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const TextureDesc desc {
|
||||||
|
.extent = Extent2D { .width = 64, .height = 32 },
|
||||||
|
.format = TextureFormat::rgba8,
|
||||||
|
.render_target = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
PP_EXPECT(h, validate_readback_region(desc, ReadbackRegion { .x = 0, .y = 0, .width = 64, .height = 32 }).ok());
|
||||||
|
PP_EXPECT(h, validate_readback_region(desc, ReadbackRegion { .x = 63, .y = 31, .width = 1, .height = 1 }).ok());
|
||||||
|
|
||||||
|
const auto empty = validate_readback_region(desc, ReadbackRegion { .x = 0, .y = 0, .width = 0, .height = 1 });
|
||||||
|
const auto origin_outside = validate_readback_region(desc, ReadbackRegion { .x = 65, .y = 0, .width = 1, .height = 1 });
|
||||||
|
const auto overrun = validate_readback_region(desc, ReadbackRegion { .x = 63, .y = 31, .width = 2, .height = 1 });
|
||||||
|
|
||||||
|
PP_EXPECT(h, !empty.ok());
|
||||||
|
PP_EXPECT(h, empty.code == StatusCode::invalid_argument);
|
||||||
|
PP_EXPECT(h, !origin_outside.ok());
|
||||||
|
PP_EXPECT(h, origin_outside.code == StatusCode::out_of_range);
|
||||||
|
PP_EXPECT(h, !overrun.ok());
|
||||||
|
PP_EXPECT(h, overrun.code == StatusCode::out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
pp::tests::Harness harness;
|
||||||
|
harness.run("computes_texture_sizes", computes_texture_sizes);
|
||||||
|
harness.run("rejects_invalid_or_excessive_extents", rejects_invalid_or_excessive_extents);
|
||||||
|
harness.run("validates_readback_bounds", validates_readback_bounds);
|
||||||
|
return harness.finish();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user