mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-05 22:23:29 +00:00
Method was renamed in https://github.com/gpuweb/gpuweb/pull/2560 Bug: dawn:1286 Change-Id: I02a5da3f6ff56868c4d8f45a17433b6c5adf5c22 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/79480 Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Brandon Jones <bajones@chromium.org>
795 lines
33 KiB
C++
795 lines
33 KiB
C++
// Copyright 2019 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "tests/DawnTest.h"
|
|
|
|
#include "dawn/common/Assert.h"
|
|
#include "dawn/common/Math.h"
|
|
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
|
|
#include "dawn/utils/TextureUtils.h"
|
|
#include "dawn/utils/WGPUHelpers.h"
|
|
|
|
#include <cmath>
|
|
#include <type_traits>
|
|
|
|
// An expectation for float buffer content that can correctly compare different NaN values and
|
|
// supports a basic tolerance for comparison of finite values.
|
|
class ExpectFloatWithTolerance : public detail::Expectation {
|
|
public:
|
|
ExpectFloatWithTolerance(std::vector<float> expected, float tolerance)
|
|
: mExpected(std::move(expected)), mTolerance(tolerance) {
|
|
}
|
|
|
|
testing::AssertionResult Check(const void* data, size_t size) override {
|
|
ASSERT(size == sizeof(float) * mExpected.size());
|
|
|
|
const float* actual = static_cast<const float*>(data);
|
|
|
|
for (size_t i = 0; i < mExpected.size(); ++i) {
|
|
float expectedValue = mExpected[i];
|
|
float actualValue = actual[i];
|
|
|
|
if (!FloatsMatch(expectedValue, actualValue)) {
|
|
testing::AssertionResult result = testing::AssertionFailure()
|
|
<< "Expected data[" << i << "] to be close to "
|
|
<< expectedValue << ", actual " << actualValue
|
|
<< std::endl;
|
|
return result;
|
|
}
|
|
}
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
private:
|
|
bool FloatsMatch(float expected, float actual) {
|
|
if (std::isnan(expected)) {
|
|
return std::isnan(actual);
|
|
}
|
|
|
|
if (std::isinf(expected)) {
|
|
return std::isinf(actual) && std::signbit(expected) == std::signbit(actual);
|
|
}
|
|
|
|
if (mTolerance == 0.0f) {
|
|
return expected == actual;
|
|
}
|
|
|
|
float error = std::abs(expected - actual);
|
|
return error < mTolerance;
|
|
}
|
|
|
|
std::vector<float> mExpected;
|
|
float mTolerance;
|
|
};
|
|
|
|
// An expectation for float16 buffers that can correctly compare NaNs (all NaNs are equivalent).
|
|
class ExpectFloat16 : public detail::Expectation {
|
|
public:
|
|
ExpectFloat16(std::vector<uint16_t> expected) : mExpected(std::move(expected)) {
|
|
}
|
|
|
|
testing::AssertionResult Check(const void* data, size_t size) override {
|
|
ASSERT(size == sizeof(uint16_t) * mExpected.size());
|
|
|
|
const uint16_t* actual = static_cast<const uint16_t*>(data);
|
|
|
|
for (size_t i = 0; i < mExpected.size(); ++i) {
|
|
uint16_t expectedValue = mExpected[i];
|
|
uint16_t actualValue = actual[i];
|
|
|
|
if (!Floats16Match(expectedValue, actualValue)) {
|
|
testing::AssertionResult result = testing::AssertionFailure()
|
|
<< "Expected data[" << i << "] to be "
|
|
<< expectedValue << ", actual " << actualValue
|
|
<< std::endl;
|
|
return result;
|
|
}
|
|
}
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
private:
|
|
bool Floats16Match(float expected, float actual) {
|
|
if (IsFloat16NaN(expected)) {
|
|
return IsFloat16NaN(actual);
|
|
}
|
|
|
|
return expected == actual;
|
|
}
|
|
|
|
std::vector<uint16_t> mExpected;
|
|
};
|
|
|
|
class TextureFormatTest : public DawnTest {
|
|
protected:
|
|
// Structure containing all the information that tests need to know about the format.
|
|
struct FormatTestInfo {
|
|
wgpu::TextureFormat format;
|
|
uint32_t texelByteSize;
|
|
wgpu::TextureComponentType type;
|
|
uint32_t componentCount;
|
|
};
|
|
|
|
// Returns a reprensentation of a format that can be used to contain the "uncompressed" values
|
|
// of the format. That the equivalent format with all channels 32bit-sized.
|
|
FormatTestInfo GetUncompressedFormatInfo(FormatTestInfo formatInfo) {
|
|
switch (formatInfo.type) {
|
|
case wgpu::TextureComponentType::Float:
|
|
return {wgpu::TextureFormat::RGBA32Float, 16, formatInfo.type, 4};
|
|
case wgpu::TextureComponentType::Sint:
|
|
return {wgpu::TextureFormat::RGBA32Sint, 16, formatInfo.type, 4};
|
|
case wgpu::TextureComponentType::Uint:
|
|
return {wgpu::TextureFormat::RGBA32Uint, 16, formatInfo.type, 4};
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
// Return a pipeline that can be used in a full-texture draw to sample from the texture in the
|
|
// bindgroup and output its decompressed values to the render target.
|
|
wgpu::RenderPipeline CreateSamplePipeline(FormatTestInfo sampleFormatInfo,
|
|
FormatTestInfo renderFormatInfo) {
|
|
utils::ComboRenderPipelineDescriptor desc;
|
|
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
|
@stage(vertex)
|
|
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
|
|
var pos = array<vec2<f32>, 3>(
|
|
vec2<f32>(-3.0, -1.0),
|
|
vec2<f32>( 3.0, -1.0),
|
|
vec2<f32>( 0.0, 2.0));
|
|
|
|
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
})");
|
|
|
|
// Compute the WGSL type of the texture's data.
|
|
const char* type = utils::GetWGSLColorTextureComponentType(sampleFormatInfo.format);
|
|
|
|
std::ostringstream fsSource;
|
|
fsSource << "@group(0) @binding(0) var myTexture : texture_2d<" << type << ">;\n";
|
|
fsSource << "struct FragmentOut {\n";
|
|
fsSource << " @location(0) color : vec4<" << type << ">;\n";
|
|
fsSource << R"(};
|
|
@stage(fragment)
|
|
fn main(@builtin(position) FragCoord : vec4<f32>) -> FragmentOut {
|
|
var output : FragmentOut;
|
|
output.color = textureLoad(myTexture, vec2<i32>(FragCoord.xy), 0);
|
|
return output;
|
|
})";
|
|
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fsSource.str().c_str());
|
|
|
|
desc.vertex.module = vsModule;
|
|
desc.cFragment.module = fsModule;
|
|
desc.cTargets[0].format = renderFormatInfo.format;
|
|
|
|
return device.CreateRenderPipeline(&desc);
|
|
}
|
|
|
|
// The sampling test uploads the sample data in a texture with the sampleFormatInfo.format.
|
|
// It then samples from it and renders the results in a texture with the
|
|
// renderFormatInfo.format format. Finally it checks that the data rendered matches
|
|
// expectedRenderData, using the cutom expectation if present.
|
|
void DoSampleTest(FormatTestInfo sampleFormatInfo,
|
|
const void* sampleData,
|
|
size_t sampleDataSize,
|
|
FormatTestInfo renderFormatInfo,
|
|
const void* expectedRenderData,
|
|
size_t expectedRenderDataSize,
|
|
detail::Expectation* customExpectation) {
|
|
// The input data should contain an exact number of texels
|
|
ASSERT(sampleDataSize % sampleFormatInfo.texelByteSize == 0);
|
|
uint32_t width = sampleDataSize / sampleFormatInfo.texelByteSize;
|
|
|
|
// The input data must be a multiple of 4 byte in length for WriteBuffer
|
|
ASSERT(sampleDataSize % 4 == 0);
|
|
ASSERT(expectedRenderDataSize % 4 == 0);
|
|
|
|
// Create the texture we will sample from
|
|
wgpu::TextureDescriptor sampleTextureDesc;
|
|
sampleTextureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding;
|
|
sampleTextureDesc.size = {width, 1, 1};
|
|
sampleTextureDesc.format = sampleFormatInfo.format;
|
|
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDesc);
|
|
|
|
wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(device, sampleData, sampleDataSize,
|
|
wgpu::BufferUsage::CopySrc);
|
|
|
|
// Create the texture that we will render results to
|
|
ASSERT(expectedRenderDataSize == width * renderFormatInfo.texelByteSize);
|
|
|
|
wgpu::TextureDescriptor renderTargetDesc;
|
|
renderTargetDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment;
|
|
renderTargetDesc.size = {width, 1, 1};
|
|
renderTargetDesc.format = renderFormatInfo.format;
|
|
|
|
wgpu::Texture renderTarget = device.CreateTexture(&renderTargetDesc);
|
|
|
|
// Create the readback buffer for the data in renderTarget
|
|
wgpu::BufferDescriptor readbackBufferDesc;
|
|
readbackBufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
|
|
readbackBufferDesc.size = expectedRenderDataSize;
|
|
wgpu::Buffer readbackBuffer = device.CreateBuffer(&readbackBufferDesc);
|
|
|
|
// Prepare objects needed to sample from texture in the renderpass
|
|
wgpu::RenderPipeline pipeline = CreateSamplePipeline(sampleFormatInfo, renderFormatInfo);
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{{0, sampleTexture.CreateView()}});
|
|
|
|
// Encode commands for the test that fill texture, sample it to render to renderTarget then
|
|
// copy renderTarget in a buffer so we can read it easily.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
{
|
|
wgpu::ImageCopyBuffer bufferView = utils::CreateImageCopyBuffer(uploadBuffer, 0, 256);
|
|
wgpu::ImageCopyTexture textureView =
|
|
utils::CreateImageCopyTexture(sampleTexture, 0, {0, 0, 0});
|
|
wgpu::Extent3D extent{width, 1, 1};
|
|
encoder.CopyBufferToTexture(&bufferView, &textureView, &extent);
|
|
}
|
|
|
|
utils::ComboRenderPassDescriptor renderPassDesc({renderTarget.CreateView()});
|
|
wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDesc);
|
|
renderPass.SetPipeline(pipeline);
|
|
renderPass.SetBindGroup(0, bindGroup);
|
|
renderPass.Draw(3);
|
|
renderPass.End();
|
|
|
|
{
|
|
wgpu::ImageCopyBuffer bufferView = utils::CreateImageCopyBuffer(readbackBuffer, 0, 256);
|
|
wgpu::ImageCopyTexture textureView =
|
|
utils::CreateImageCopyTexture(renderTarget, 0, {0, 0, 0});
|
|
wgpu::Extent3D extent{width, 1, 1};
|
|
encoder.CopyTextureToBuffer(&textureView, &bufferView, &extent);
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
// For floats use a special expectation that understands how to compare NaNs and support a
|
|
// tolerance.
|
|
if (customExpectation != nullptr) {
|
|
AddBufferExpectation(__FILE__, __LINE__, readbackBuffer, 0, expectedRenderDataSize,
|
|
customExpectation);
|
|
} else {
|
|
EXPECT_BUFFER_U32_RANGE_EQ(static_cast<const uint32_t*>(expectedRenderData),
|
|
readbackBuffer, 0,
|
|
expectedRenderDataSize / sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
template <typename Data>
|
|
std::vector<Data> ExpandDataTo4Component(const std::vector<Data>& originalData,
|
|
uint32_t originalComponentCount,
|
|
const std::array<Data, 4>& defaultValues) {
|
|
std::vector<Data> result;
|
|
|
|
for (size_t i = 0; i < originalData.size() / originalComponentCount; i++) {
|
|
for (size_t component = 0; component < 4; component++) {
|
|
if (component < originalComponentCount) {
|
|
result.push_back(originalData[i * originalComponentCount + component]);
|
|
} else {
|
|
result.push_back(defaultValues[component]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Helper functions used to run tests that convert the typeful test objects to typeless void*
|
|
|
|
template <typename TextureData, typename RenderData>
|
|
void DoFormatSamplingTest(FormatTestInfo formatInfo,
|
|
const std::vector<TextureData>& textureData,
|
|
const std::vector<RenderData>& expectedRenderData,
|
|
detail::Expectation* customExpectation = nullptr) {
|
|
FormatTestInfo renderFormatInfo = GetUncompressedFormatInfo(formatInfo);
|
|
|
|
// Expand the expected data to be 4 component wide with the default sampling values of
|
|
// (0, 0, 0, 1)
|
|
std::array<RenderData, 4> defaultValues = {RenderData(0), RenderData(0), RenderData(0),
|
|
RenderData(1)};
|
|
std::vector<RenderData> expandedRenderData =
|
|
ExpandDataTo4Component(expectedRenderData, formatInfo.componentCount, defaultValues);
|
|
|
|
DoSampleTest(formatInfo, textureData.data(), textureData.size() * sizeof(TextureData),
|
|
renderFormatInfo, expandedRenderData.data(),
|
|
expandedRenderData.size() * sizeof(RenderData), customExpectation);
|
|
}
|
|
|
|
template <typename TextureData>
|
|
void DoFloatFormatSamplingTest(FormatTestInfo formatInfo,
|
|
const std::vector<TextureData>& textureData,
|
|
const std::vector<float>& expectedRenderData,
|
|
float floatTolerance = 0.0f) {
|
|
// Expand the expected data to be 4 component wide with the default sampling values of
|
|
// (0, 0, 0, 1)
|
|
std::array<float, 4> defaultValues = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
std::vector<float> expandedRenderData =
|
|
ExpandDataTo4Component(expectedRenderData, formatInfo.componentCount, defaultValues);
|
|
|
|
// Use a special expectation that understands how to compare NaNs and supports a tolerance.
|
|
DoFormatSamplingTest(formatInfo, textureData, expectedRenderData,
|
|
new ExpectFloatWithTolerance(expandedRenderData, floatTolerance));
|
|
}
|
|
|
|
template <typename TextureData, typename RenderData>
|
|
void DoFormatRenderingTest(FormatTestInfo formatInfo,
|
|
const std::vector<TextureData>& textureData,
|
|
const std::vector<RenderData>& expectedRenderData,
|
|
detail::Expectation* customExpectation = nullptr) {
|
|
FormatTestInfo sampleFormatInfo = GetUncompressedFormatInfo(formatInfo);
|
|
|
|
// Expand the sampling texture data to contain garbage data for unused components to check
|
|
// that they don't influence the rendering result.
|
|
std::array<TextureData, 4> garbageValues;
|
|
garbageValues.fill(13);
|
|
std::vector<TextureData> expandedTextureData =
|
|
ExpandDataTo4Component(textureData, formatInfo.componentCount, garbageValues);
|
|
|
|
DoSampleTest(sampleFormatInfo, expandedTextureData.data(),
|
|
expandedTextureData.size() * sizeof(TextureData), formatInfo,
|
|
expectedRenderData.data(), expectedRenderData.size() * sizeof(RenderData),
|
|
customExpectation);
|
|
}
|
|
|
|
// Below are helper functions for types that are very similar to one another so the logic is
|
|
// shared.
|
|
|
|
template <typename T>
|
|
void DoUnormTest(FormatTestInfo formatInfo) {
|
|
static_assert(!std::is_signed<T>::value && std::is_integral<T>::value);
|
|
ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
|
|
ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
|
|
|
|
T maxValue = std::numeric_limits<T>::max();
|
|
std::vector<T> textureData = {0, 1, maxValue, maxValue};
|
|
std::vector<float> uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f};
|
|
|
|
DoFormatSamplingTest(formatInfo, textureData, uncompressedData);
|
|
DoFormatRenderingTest(formatInfo, uncompressedData, textureData);
|
|
}
|
|
|
|
template <typename T>
|
|
void DoSnormTest(FormatTestInfo formatInfo) {
|
|
static_assert(std::is_signed<T>::value && std::is_integral<T>::value);
|
|
ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
|
|
ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
|
|
|
|
T maxValue = std::numeric_limits<T>::max();
|
|
T minValue = std::numeric_limits<T>::min();
|
|
std::vector<T> textureData = {0, 1, -1, maxValue, minValue, T(minValue + 1), 0, 0};
|
|
std::vector<float> uncompressedData = {
|
|
0.0f, 1.0f / maxValue, -1.0f / maxValue, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f};
|
|
|
|
DoFloatFormatSamplingTest(formatInfo, textureData, uncompressedData, 0.0001f / maxValue);
|
|
// Snorm formats aren't renderable because they are not guaranteed renderable in Vulkan
|
|
}
|
|
|
|
template <typename T>
|
|
void DoUintTest(FormatTestInfo formatInfo) {
|
|
static_assert(!std::is_signed<T>::value && std::is_integral<T>::value);
|
|
ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
|
|
ASSERT(formatInfo.type == wgpu::TextureComponentType::Uint);
|
|
|
|
T maxValue = std::numeric_limits<T>::max();
|
|
std::vector<T> textureData = {0, 1, maxValue, maxValue};
|
|
std::vector<uint32_t> uncompressedData = {0, 1, maxValue, maxValue};
|
|
|
|
DoFormatSamplingTest(formatInfo, textureData, uncompressedData);
|
|
DoFormatRenderingTest(formatInfo, uncompressedData, textureData);
|
|
}
|
|
|
|
template <typename T>
|
|
void DoSintTest(FormatTestInfo formatInfo) {
|
|
static_assert(std::is_signed<T>::value && std::is_integral<T>::value);
|
|
ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
|
|
ASSERT(formatInfo.type == wgpu::TextureComponentType::Sint);
|
|
|
|
T maxValue = std::numeric_limits<T>::max();
|
|
T minValue = std::numeric_limits<T>::min();
|
|
std::vector<T> textureData = {0, 1, maxValue, minValue};
|
|
std::vector<int32_t> uncompressedData = {0, 1, maxValue, minValue};
|
|
|
|
DoFormatSamplingTest(formatInfo, textureData, uncompressedData);
|
|
DoFormatRenderingTest(formatInfo, uncompressedData, textureData);
|
|
}
|
|
|
|
void DoFloat32Test(FormatTestInfo formatInfo) {
|
|
ASSERT(sizeof(float) * formatInfo.componentCount == formatInfo.texelByteSize);
|
|
ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
|
|
|
|
std::vector<float> textureData = {+0.0f, -0.0f, 1.0f, 1.0e-29f,
|
|
1.0e29f, NAN, INFINITY, -INFINITY};
|
|
|
|
DoFloatFormatSamplingTest(formatInfo, textureData, textureData);
|
|
DoFormatRenderingTest(formatInfo, textureData, textureData,
|
|
new ExpectFloatWithTolerance(textureData, 0.0f));
|
|
}
|
|
|
|
void DoFloat16Test(FormatTestInfo formatInfo) {
|
|
ASSERT(sizeof(int16_t) * formatInfo.componentCount == formatInfo.texelByteSize);
|
|
ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
|
|
|
|
std::vector<float> uncompressedData = {+0.0f, -0.0f, 1.0f, 1.01e-4f,
|
|
1.0e4f, NAN, INFINITY, -INFINITY};
|
|
std::vector<uint16_t> textureData;
|
|
for (float value : uncompressedData) {
|
|
textureData.push_back(Float32ToFloat16(value));
|
|
}
|
|
|
|
DoFloatFormatSamplingTest(formatInfo, textureData, uncompressedData, 1.0e-5f);
|
|
|
|
// Use a special expectation that knows that all Float16 NaNs are equivalent.
|
|
DoFormatRenderingTest(formatInfo, uncompressedData, textureData,
|
|
new ExpectFloat16(textureData));
|
|
}
|
|
};
|
|
|
|
// Test the R8Unorm format
|
|
TEST_P(TextureFormatTest, R8Unorm) {
|
|
DoUnormTest<uint8_t>({wgpu::TextureFormat::R8Unorm, 1, wgpu::TextureComponentType::Float, 1});
|
|
}
|
|
|
|
// Test the RG8Unorm format
|
|
TEST_P(TextureFormatTest, RG8Unorm) {
|
|
DoUnormTest<uint8_t>({wgpu::TextureFormat::RG8Unorm, 2, wgpu::TextureComponentType::Float, 2});
|
|
}
|
|
|
|
// Test the RGBA8Unorm format
|
|
TEST_P(TextureFormatTest, RGBA8Unorm) {
|
|
DoUnormTest<uint8_t>(
|
|
{wgpu::TextureFormat::RGBA8Unorm, 4, wgpu::TextureComponentType::Float, 4});
|
|
}
|
|
|
|
// Test the BGRA8Unorm format
|
|
TEST_P(TextureFormatTest, BGRA8Unorm) {
|
|
// TODO(crbug.com/dawn/596): BGRA is unsupported on OpenGL ES; add workaround or validation
|
|
DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
|
|
uint8_t maxValue = std::numeric_limits<uint8_t>::max();
|
|
std::vector<uint8_t> textureData = {maxValue, 1, 0, maxValue};
|
|
std::vector<float> uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f};
|
|
DoFormatSamplingTest({wgpu::TextureFormat::BGRA8Unorm, 4, wgpu::TextureComponentType::Float, 4},
|
|
textureData, uncompressedData);
|
|
DoFormatRenderingTest(
|
|
{wgpu::TextureFormat::BGRA8Unorm, 4, wgpu::TextureComponentType::Float, 4},
|
|
uncompressedData, textureData);
|
|
}
|
|
|
|
// Test the R8Snorm format
|
|
TEST_P(TextureFormatTest, R8Snorm) {
|
|
DoSnormTest<int8_t>({wgpu::TextureFormat::R8Snorm, 1, wgpu::TextureComponentType::Float, 1});
|
|
}
|
|
|
|
// Test the RG8Snorm format
|
|
TEST_P(TextureFormatTest, RG8Snorm) {
|
|
DoSnormTest<int8_t>({wgpu::TextureFormat::RG8Snorm, 2, wgpu::TextureComponentType::Float, 2});
|
|
}
|
|
|
|
// Test the RGBA8Snorm format
|
|
TEST_P(TextureFormatTest, RGBA8Snorm) {
|
|
DoSnormTest<int8_t>({wgpu::TextureFormat::RGBA8Snorm, 4, wgpu::TextureComponentType::Float, 4});
|
|
}
|
|
|
|
// Test the R8Uint format
|
|
TEST_P(TextureFormatTest, R8Uint) {
|
|
DoUintTest<uint8_t>({wgpu::TextureFormat::R8Uint, 1, wgpu::TextureComponentType::Uint, 1});
|
|
}
|
|
|
|
// Test the RG8Uint format
|
|
TEST_P(TextureFormatTest, RG8Uint) {
|
|
DoUintTest<uint8_t>({wgpu::TextureFormat::RG8Uint, 2, wgpu::TextureComponentType::Uint, 2});
|
|
}
|
|
|
|
// Test the RGBA8Uint format
|
|
TEST_P(TextureFormatTest, RGBA8Uint) {
|
|
DoUintTest<uint8_t>({wgpu::TextureFormat::RGBA8Uint, 4, wgpu::TextureComponentType::Uint, 4});
|
|
}
|
|
|
|
// Test the R16Uint format
|
|
TEST_P(TextureFormatTest, R16Uint) {
|
|
DoUintTest<uint16_t>({wgpu::TextureFormat::R16Uint, 2, wgpu::TextureComponentType::Uint, 1});
|
|
}
|
|
|
|
// Test the RG16Uint format
|
|
TEST_P(TextureFormatTest, RG16Uint) {
|
|
DoUintTest<uint16_t>({wgpu::TextureFormat::RG16Uint, 4, wgpu::TextureComponentType::Uint, 2});
|
|
}
|
|
|
|
// Test the RGBA16Uint format
|
|
TEST_P(TextureFormatTest, RGBA16Uint) {
|
|
DoUintTest<uint16_t>({wgpu::TextureFormat::RGBA16Uint, 8, wgpu::TextureComponentType::Uint, 4});
|
|
}
|
|
|
|
// Test the R32Uint format
|
|
TEST_P(TextureFormatTest, R32Uint) {
|
|
DoUintTest<uint32_t>({wgpu::TextureFormat::R32Uint, 4, wgpu::TextureComponentType::Uint, 1});
|
|
}
|
|
|
|
// Test the RG32Uint format
|
|
TEST_P(TextureFormatTest, RG32Uint) {
|
|
DoUintTest<uint32_t>({wgpu::TextureFormat::RG32Uint, 8, wgpu::TextureComponentType::Uint, 2});
|
|
}
|
|
|
|
// Test the RGBA32Uint format
|
|
TEST_P(TextureFormatTest, RGBA32Uint) {
|
|
DoUintTest<uint32_t>(
|
|
{wgpu::TextureFormat::RGBA32Uint, 16, wgpu::TextureComponentType::Uint, 4});
|
|
}
|
|
|
|
// Test the R8Sint format
|
|
TEST_P(TextureFormatTest, R8Sint) {
|
|
DoSintTest<int8_t>({wgpu::TextureFormat::R8Sint, 1, wgpu::TextureComponentType::Sint, 1});
|
|
}
|
|
|
|
// Test the RG8Sint format
|
|
TEST_P(TextureFormatTest, RG8Sint) {
|
|
DoSintTest<int8_t>({wgpu::TextureFormat::RG8Sint, 2, wgpu::TextureComponentType::Sint, 2});
|
|
}
|
|
|
|
// Test the RGBA8Sint format
|
|
TEST_P(TextureFormatTest, RGBA8Sint) {
|
|
DoSintTest<int8_t>({wgpu::TextureFormat::RGBA8Sint, 4, wgpu::TextureComponentType::Sint, 4});
|
|
}
|
|
|
|
// Test the R16Sint format
|
|
TEST_P(TextureFormatTest, R16Sint) {
|
|
DoSintTest<int16_t>({wgpu::TextureFormat::R16Sint, 2, wgpu::TextureComponentType::Sint, 1});
|
|
}
|
|
|
|
// Test the RG16Sint format
|
|
TEST_P(TextureFormatTest, RG16Sint) {
|
|
DoSintTest<int16_t>({wgpu::TextureFormat::RG16Sint, 4, wgpu::TextureComponentType::Sint, 2});
|
|
}
|
|
|
|
// Test the RGBA16Sint format
|
|
TEST_P(TextureFormatTest, RGBA16Sint) {
|
|
DoSintTest<int16_t>({wgpu::TextureFormat::RGBA16Sint, 8, wgpu::TextureComponentType::Sint, 4});
|
|
}
|
|
|
|
// Test the R32Sint format
|
|
TEST_P(TextureFormatTest, R32Sint) {
|
|
DoSintTest<int32_t>({wgpu::TextureFormat::R32Sint, 4, wgpu::TextureComponentType::Sint, 1});
|
|
}
|
|
|
|
// Test the RG32Sint format
|
|
TEST_P(TextureFormatTest, RG32Sint) {
|
|
DoSintTest<int32_t>({wgpu::TextureFormat::RG32Sint, 8, wgpu::TextureComponentType::Sint, 2});
|
|
}
|
|
|
|
// Test the RGBA32Sint format
|
|
TEST_P(TextureFormatTest, RGBA32Sint) {
|
|
DoSintTest<int32_t>({wgpu::TextureFormat::RGBA32Sint, 16, wgpu::TextureComponentType::Sint, 4});
|
|
}
|
|
|
|
// Test the R32Float format
|
|
TEST_P(TextureFormatTest, R32Float) {
|
|
DoFloat32Test({wgpu::TextureFormat::R32Float, 4, wgpu::TextureComponentType::Float, 1});
|
|
}
|
|
|
|
// Test the RG32Float format
|
|
TEST_P(TextureFormatTest, RG32Float) {
|
|
DoFloat32Test({wgpu::TextureFormat::RG32Float, 8, wgpu::TextureComponentType::Float, 2});
|
|
}
|
|
|
|
// Test the RGBA32Float format
|
|
TEST_P(TextureFormatTest, RGBA32Float) {
|
|
DoFloat32Test({wgpu::TextureFormat::RGBA32Float, 16, wgpu::TextureComponentType::Float, 4});
|
|
}
|
|
|
|
// Test the R16Float format
|
|
TEST_P(TextureFormatTest, R16Float) {
|
|
// TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by
|
|
// swiftshader
|
|
DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsSwiftshader() || IsANGLE());
|
|
|
|
DoFloat16Test({wgpu::TextureFormat::R16Float, 2, wgpu::TextureComponentType::Float, 1});
|
|
}
|
|
|
|
// Test the RG16Float format
|
|
TEST_P(TextureFormatTest, RG16Float) {
|
|
// TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by
|
|
// swiftshader
|
|
DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsSwiftshader() || IsANGLE());
|
|
|
|
DoFloat16Test({wgpu::TextureFormat::RG16Float, 4, wgpu::TextureComponentType::Float, 2});
|
|
}
|
|
|
|
// Test the RGBA16Float format
|
|
TEST_P(TextureFormatTest, RGBA16Float) {
|
|
// TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by
|
|
// swiftshader
|
|
DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsSwiftshader() || IsANGLE());
|
|
|
|
DoFloat16Test({wgpu::TextureFormat::RGBA16Float, 8, wgpu::TextureComponentType::Float, 4});
|
|
}
|
|
|
|
// Test the RGBA8Unorm format
|
|
TEST_P(TextureFormatTest, RGBA8UnormSrgb) {
|
|
uint8_t maxValue = std::numeric_limits<uint8_t>::max();
|
|
std::vector<uint8_t> textureData = {0, 1, maxValue, 64, 35, 68, 152, 168};
|
|
|
|
std::vector<float> uncompressedData;
|
|
for (size_t i = 0; i < textureData.size(); i += 4) {
|
|
uncompressedData.push_back(SRGBToLinear(textureData[i + 0] / float(maxValue)));
|
|
uncompressedData.push_back(SRGBToLinear(textureData[i + 1] / float(maxValue)));
|
|
uncompressedData.push_back(SRGBToLinear(textureData[i + 2] / float(maxValue)));
|
|
// Alpha is linear for sRGB formats
|
|
uncompressedData.push_back(textureData[i + 3] / float(maxValue));
|
|
}
|
|
|
|
DoFloatFormatSamplingTest(
|
|
{wgpu::TextureFormat::RGBA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, textureData,
|
|
uncompressedData, 1.0e-3);
|
|
DoFormatRenderingTest(
|
|
{wgpu::TextureFormat::RGBA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4},
|
|
uncompressedData, textureData);
|
|
}
|
|
|
|
// Test the BGRA8UnormSrgb format
|
|
TEST_P(TextureFormatTest, BGRA8UnormSrgb) {
|
|
// TODO(cwallez@chromium.org): This format doesn't exist in OpenGL, emulate it using
|
|
// RGBA8UnormSrgb and swizzling / shader twiddling
|
|
DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
|
|
|
|
uint8_t maxValue = std::numeric_limits<uint8_t>::max();
|
|
std::vector<uint8_t> textureData = {0, 1, maxValue, 64, 35, 68, 152, 168};
|
|
|
|
std::vector<float> uncompressedData;
|
|
for (size_t i = 0; i < textureData.size(); i += 4) {
|
|
// Note that R and B are swapped
|
|
uncompressedData.push_back(SRGBToLinear(textureData[i + 2] / float(maxValue)));
|
|
uncompressedData.push_back(SRGBToLinear(textureData[i + 1] / float(maxValue)));
|
|
uncompressedData.push_back(SRGBToLinear(textureData[i + 0] / float(maxValue)));
|
|
// Alpha is linear for sRGB formats
|
|
uncompressedData.push_back(textureData[i + 3] / float(maxValue));
|
|
}
|
|
|
|
DoFloatFormatSamplingTest(
|
|
{wgpu::TextureFormat::BGRA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, textureData,
|
|
uncompressedData, 1.0e-3);
|
|
DoFormatRenderingTest(
|
|
{wgpu::TextureFormat::BGRA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4},
|
|
uncompressedData, textureData);
|
|
}
|
|
|
|
// Test the RGB10A2Unorm format
|
|
TEST_P(TextureFormatTest, RGB10A2Unorm) {
|
|
auto MakeRGB10A2 = [](uint32_t r, uint32_t g, uint32_t b, uint32_t a) -> uint32_t {
|
|
ASSERT((r & 0x3FF) == r);
|
|
ASSERT((g & 0x3FF) == g);
|
|
ASSERT((b & 0x3FF) == b);
|
|
ASSERT((a & 0x3) == a);
|
|
return r | g << 10 | b << 20 | a << 30;
|
|
};
|
|
|
|
std::vector<uint32_t> textureData = {MakeRGB10A2(0, 0, 0, 0), MakeRGB10A2(1023, 1023, 1023, 1),
|
|
MakeRGB10A2(243, 576, 765, 2), MakeRGB10A2(0, 0, 0, 3)};
|
|
// clang-format off
|
|
std::vector<float> uncompressedData = {
|
|
0.0f, 0.0f, 0.0f, 0.0f,
|
|
1.0f, 1.0f, 1.0f, 1 / 3.0f,
|
|
243 / 1023.0f, 576 / 1023.0f, 765 / 1023.0f, 2 / 3.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f
|
|
};
|
|
// clang-format on
|
|
|
|
DoFloatFormatSamplingTest(
|
|
{wgpu::TextureFormat::RGB10A2Unorm, 4, wgpu::TextureComponentType::Float, 4}, textureData,
|
|
uncompressedData, 1.0e-5);
|
|
DoFormatRenderingTest(
|
|
{wgpu::TextureFormat::RGB10A2Unorm, 4, wgpu::TextureComponentType::Float, 4},
|
|
uncompressedData, textureData);
|
|
}
|
|
|
|
// Test the RG11B10Ufloat format
|
|
TEST_P(TextureFormatTest, RG11B10Ufloat) {
|
|
constexpr uint32_t kFloat11Zero = 0;
|
|
constexpr uint32_t kFloat11Infinity = 0x7C0;
|
|
constexpr uint32_t kFloat11Nan = 0x7C1;
|
|
constexpr uint32_t kFloat11One = 0x3C0;
|
|
|
|
constexpr uint32_t kFloat10Zero = 0;
|
|
constexpr uint32_t kFloat10Infinity = 0x3E0;
|
|
constexpr uint32_t kFloat10Nan = 0x3E1;
|
|
constexpr uint32_t kFloat10One = 0x1E0;
|
|
|
|
auto MakeRG11B10 = [](uint32_t r, uint32_t g, uint32_t b) {
|
|
ASSERT((r & 0x7FF) == r);
|
|
ASSERT((g & 0x7FF) == g);
|
|
ASSERT((b & 0x3FF) == b);
|
|
return r | g << 11 | b << 22;
|
|
};
|
|
|
|
// Test each of (0, 1, INFINITY, NaN) for each component but never two with the same value at a
|
|
// time.
|
|
std::vector<uint32_t> textureData = {
|
|
MakeRG11B10(kFloat11Zero, kFloat11Infinity, kFloat10Nan),
|
|
MakeRG11B10(kFloat11Infinity, kFloat11Nan, kFloat10One),
|
|
MakeRG11B10(kFloat11Nan, kFloat11One, kFloat10Zero),
|
|
MakeRG11B10(kFloat11One, kFloat11Zero, kFloat10Infinity),
|
|
};
|
|
|
|
// This is one of the only 3-channel formats, so we don't have specific testing for them. Alpha
|
|
// should always be sampled as 1
|
|
// clang-format off
|
|
std::vector<float> uncompressedData = {
|
|
0.0f, INFINITY, NAN, 1.0f,
|
|
INFINITY, NAN, 1.0f, 1.0f,
|
|
NAN, 1.0f, 0.0f, 1.0f,
|
|
1.0f, 0.0f, INFINITY, 1.0f
|
|
};
|
|
// clang-format on
|
|
|
|
DoFloatFormatSamplingTest(
|
|
{wgpu::TextureFormat::RG11B10Ufloat, 4, wgpu::TextureComponentType::Float, 4}, textureData,
|
|
uncompressedData);
|
|
// This format is not renderable.
|
|
}
|
|
|
|
// Test the RGB9E5Ufloat format
|
|
TEST_P(TextureFormatTest, RGB9E5Ufloat) {
|
|
// RGB9E5 is different from other floating point formats because the mantissa doesn't index in
|
|
// the window defined by the exponent but is instead treated as a pure multiplier. There is
|
|
// also no Infinity or NaN. The OpenGL 4.6 spec has the best explanation I've found in section
|
|
// 8.25 "Shared Exponent Texture Color Conversion":
|
|
//
|
|
// red = reduint * 2^(expuint - B - N) = reduint * 2^(expuint - 24)
|
|
//
|
|
// Where reduint and expuint are the integer values when considering the E5 as a 5bit uint, and
|
|
// the r9 as a 9bit uint. B the number of bits of the mantissa (9), and N the offset for the
|
|
// exponent (15).
|
|
|
|
float smallestExponent = std::pow(2.0f, -24.0f);
|
|
float largestExponent = std::pow(2.0f, float(31 - 24));
|
|
|
|
auto MakeRGB9E5 = [](uint32_t r, uint32_t g, uint32_t b, uint32_t e) {
|
|
ASSERT((r & 0x1FF) == r);
|
|
ASSERT((g & 0x1FF) == g);
|
|
ASSERT((b & 0x1FF) == b);
|
|
ASSERT((e & 0x1F) == e);
|
|
return r | g << 9 | b << 18 | e << 27;
|
|
};
|
|
|
|
// Test the smallest largest, and "1" exponents
|
|
std::vector<uint32_t> textureData = {
|
|
MakeRGB9E5(0, 1, 2, 0b00000),
|
|
MakeRGB9E5(2, 1, 0, 0b11111),
|
|
MakeRGB9E5(0, 1, 2, 0b11000),
|
|
};
|
|
|
|
// This is one of the only 3-channel formats, so we don't have specific testing for them. Alpha
|
|
// should always be sampled as 1
|
|
// clang-format off
|
|
std::vector<float> uncompressedData = {
|
|
0.0f, smallestExponent, 2.0f * smallestExponent, 1.0f,
|
|
2.0f * largestExponent, largestExponent, 0.0f, 1.0f,
|
|
0.0f, 1.0f, 2.0f, 1.0f,
|
|
};
|
|
// clang-format on
|
|
|
|
DoFloatFormatSamplingTest(
|
|
{wgpu::TextureFormat::RGB9E5Ufloat, 4, wgpu::TextureComponentType::Float, 4}, textureData,
|
|
uncompressedData);
|
|
// This format is not renderable.
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(TextureFormatTest,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|