mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-24 15:36:33 +00:00
Add texture zoo tests
* These tests ensure that texture rendering works correctly for all different types of texture types, and for all formats, across different APIs, including across a remote-proxy connection.
This commit is contained in:
@@ -29,6 +29,7 @@ set(VULKAN_SRC
|
||||
vk/vk_spec_constants.cpp
|
||||
vk/vk_spirv_13_shaders.cpp
|
||||
vk/vk_structured_buffer_nested.cpp
|
||||
vk/vk_texture_zoo.cpp
|
||||
vk/vk_triangle_fan.cpp
|
||||
vk/vk_vertex_attr_zoo.cpp
|
||||
vk/vk_video_textures.cpp
|
||||
@@ -58,12 +59,14 @@ set(OPENGL_SRC
|
||||
gl/gl_shader_editing.cpp
|
||||
gl/gl_simple_triangle.cpp
|
||||
gl/gl_spirv_shader.cpp
|
||||
gl/gl_texture_zoo.cpp
|
||||
gl/gl_unsized_ms_fbo_attachment.cpp
|
||||
gl/gl_vao_0.cpp
|
||||
gl/gl_vertex_attr_zoo.cpp)
|
||||
|
||||
set(SRC main.cpp
|
||||
test_common.cpp
|
||||
texture_zoo.cpp
|
||||
linux/linux_platform.cpp
|
||||
linux/linux_window.cpp)
|
||||
|
||||
|
||||
@@ -325,15 +325,16 @@ private:
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void SetDebugName(T pObj, const char *name)
|
||||
inline void SetDebugName(T pObj, const std::string &name)
|
||||
{
|
||||
if(pObj)
|
||||
pObj->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)strlen(name), name);
|
||||
pObj->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)name.size(), name.c_str());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void SetDebugName(T pObj, const wchar_t *name)
|
||||
inline void SetDebugName(T pObj, const std::wstring &name)
|
||||
{
|
||||
if(pObj)
|
||||
pObj->SetPrivateData(WKPDID_D3DDebugObjectNameW, UINT(wcslen(name) * sizeof(wchar_t)), name);
|
||||
pObj->SetPrivateData(WKPDID_D3DDebugObjectNameW, UINT(name.size() * sizeof(wchar_t)),
|
||||
name.c_str());
|
||||
}
|
||||
|
||||
@@ -334,6 +334,24 @@ void D3D11GraphicsTest::Present()
|
||||
swap->Present(0, 0);
|
||||
}
|
||||
|
||||
void D3D11GraphicsTest::pushMarker(const std::string &name)
|
||||
{
|
||||
if(annot)
|
||||
annot->BeginEvent(UTF82Wide(name).c_str());
|
||||
}
|
||||
|
||||
void D3D11GraphicsTest::setMarker(const std::string &name)
|
||||
{
|
||||
if(annot)
|
||||
annot->SetMarker(UTF82Wide(name).c_str());
|
||||
}
|
||||
|
||||
void D3D11GraphicsTest::popMarker()
|
||||
{
|
||||
if(annot)
|
||||
annot->EndEvent();
|
||||
}
|
||||
|
||||
std::vector<byte> D3D11GraphicsTest::GetBufferData(ID3D11Buffer *buffer, uint32_t offset, uint32_t len)
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
|
||||
@@ -152,6 +152,10 @@ struct D3D11GraphicsTest : public GraphicsTest
|
||||
bool Running();
|
||||
void Present();
|
||||
|
||||
void pushMarker(const std::string &name);
|
||||
void setMarker(const std::string &name);
|
||||
void popMarker();
|
||||
|
||||
DXGI_FORMAT backbufferFmt = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
int backbufferCount = 2;
|
||||
int backbufferMSAA = 1;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -700,8 +700,6 @@ D3D12ViewCreator &D3D12ViewCreator::FirstMip(UINT mip)
|
||||
{
|
||||
if(firstMip)
|
||||
*firstMip = mip;
|
||||
else
|
||||
TEST_ERROR("This view & resource doesn't support FirstMip");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -709,8 +707,6 @@ D3D12ViewCreator &D3D12ViewCreator::NumMips(UINT num)
|
||||
{
|
||||
if(numMips)
|
||||
*numMips = num;
|
||||
else
|
||||
TEST_ERROR("This view & resource doesn't support NumMips");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -718,8 +714,6 @@ D3D12ViewCreator &D3D12ViewCreator::FirstSlice(UINT mip)
|
||||
{
|
||||
if(firstSlice)
|
||||
*firstSlice = mip;
|
||||
else
|
||||
TEST_ERROR("This view & resource doesn't support FirstSlice");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -727,8 +721,6 @@ D3D12ViewCreator &D3D12ViewCreator::NumSlices(UINT num)
|
||||
{
|
||||
if(numSlices)
|
||||
*numSlices = num;
|
||||
else
|
||||
TEST_ERROR("This view & resource doesn't support NumSlices");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -742,8 +734,6 @@ D3D12ViewCreator &D3D12ViewCreator::PlaneSlice(UINT plane)
|
||||
{
|
||||
if(planeSlice)
|
||||
*planeSlice = plane;
|
||||
else
|
||||
TEST_ERROR("This view & resource doesn't support NumSlices");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -874,6 +864,16 @@ D3D12PSOCreator::D3D12PSOCreator(D3D12GraphicsTest *test) : m_Test(test)
|
||||
GraphicsDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
GraphicsDesc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
GraphicsDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
GraphicsDesc.DepthStencilState.DepthEnable = FALSE;
|
||||
GraphicsDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
|
||||
GraphicsDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
|
||||
GraphicsDesc.DepthStencilState.StencilReadMask = 0xff;
|
||||
GraphicsDesc.DepthStencilState.StencilWriteMask = 0xff;
|
||||
GraphicsDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
|
||||
GraphicsDesc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
|
||||
GraphicsDesc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
|
||||
GraphicsDesc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
|
||||
GraphicsDesc.DepthStencilState.BackFace = GraphicsDesc.DepthStencilState.FrontFace;
|
||||
}
|
||||
|
||||
D3D12PSOCreator &D3D12PSOCreator::VS(ID3DBlobPtr blob)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -152,6 +152,7 @@
|
||||
<ClCompile Include="d3d11\d3d11_structured_buffer_read.cpp" />
|
||||
<ClCompile Include="d3d11\d3d11_test.cpp" />
|
||||
<ClCompile Include="d3d11\d3d11_texture_3d.cpp" />
|
||||
<ClCompile Include="d3d11\d3d11_texture_zoo.cpp" />
|
||||
<ClCompile Include="d3d11\d3d11_untyped_backbuffer_descriptor.cpp" />
|
||||
<ClCompile Include="d3d11\d3d11_vertex_attr_zoo.cpp" />
|
||||
<ClCompile Include="d3d11\d3d11_video_textures.cpp" />
|
||||
@@ -161,6 +162,7 @@
|
||||
<ClCompile Include="d3d12\d3d12_resource_lifetimes.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_simple_triangle.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_test.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_texture_zoo.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_untyped_backbuffer_descriptor.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_vertex_attr_zoo.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_video_textures.cpp" />
|
||||
@@ -200,6 +202,7 @@
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gl\gl_test_win32.cpp" />
|
||||
<ClCompile Include="gl\gl_texture_zoo.cpp" />
|
||||
<ClCompile Include="gl\gl_unsized_ms_fbo_attachment.cpp" />
|
||||
<ClCompile Include="gl\gl_vao_0.cpp" />
|
||||
<ClCompile Include="gl\gl_vertex_attr_zoo.cpp" />
|
||||
@@ -212,6 +215,7 @@
|
||||
<ClCompile Include="3rdparty\lz4\lz4.c" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="test_common.cpp" />
|
||||
<ClCompile Include="texture_zoo.cpp" />
|
||||
<ClCompile Include="vk\vk_parameter_zoo.cpp" />
|
||||
<ClCompile Include="vk\vk_imageless_framebuffer.cpp" />
|
||||
<ClCompile Include="vk\vk_image_layouts.cpp" />
|
||||
@@ -222,6 +226,7 @@
|
||||
<ClCompile Include="vk\vk_shader_editing.cpp" />
|
||||
<ClCompile Include="vk\vk_spec_constants.cpp" />
|
||||
<ClCompile Include="vk\vk_spirv_13_shaders.cpp" />
|
||||
<ClCompile Include="vk\vk_texture_zoo.cpp" />
|
||||
<ClCompile Include="vk\vk_triangle_fan.cpp" />
|
||||
<ClCompile Include="vk\vk_vertex_attr_zoo.cpp" />
|
||||
<ClCompile Include="vk\vk_buffer_address.cpp" />
|
||||
|
||||
@@ -348,6 +348,19 @@
|
||||
<ClCompile Include="d3d12\d3d12_write_subresource.cpp">
|
||||
<Filter>D3D12\demos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="d3d11\d3d11_texture_zoo.cpp">
|
||||
<Filter>D3D11\demos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gl\gl_texture_zoo.cpp">
|
||||
<Filter>OpenGL\demos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="texture_zoo.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_texture_zoo.cpp">
|
||||
<Filter>D3D12\demos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vk\vk_texture_zoo.cpp">
|
||||
<Filter>Vulkan\demos</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="D3D11">
|
||||
|
||||
@@ -0,0 +1,985 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "gl_test.h"
|
||||
|
||||
using namespace TextureZoo;
|
||||
|
||||
RD_TEST(GL_Texture_Zoo, OpenGLGraphicsTest)
|
||||
{
|
||||
static constexpr const char *Description =
|
||||
"Tests all possible combinations of texture type and format that are supported.";
|
||||
|
||||
std::string blitVertex = R"EOSHADER(
|
||||
#version 420 core
|
||||
|
||||
void main()
|
||||
{
|
||||
const vec4 verts[4] = vec4[4](vec4(-1.0, -1.0, 0.5, 1.0), vec4(3.0, -1.0, 0.5, 1.0),
|
||||
vec4(-1.0, 3.0, 0.5, 1.0), vec4(1.0, 1.0, 0.5, 1.0));
|
||||
|
||||
gl_Position = verts[gl_VertexID];
|
||||
}
|
||||
|
||||
)EOSHADER";
|
||||
|
||||
std::string pixelTemplate = R"EOSHADER(
|
||||
#version 420 core
|
||||
|
||||
layout(binding = 0) uniform &texdecl intex;
|
||||
|
||||
layout(location = 0, index = 0) out vec4 Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
Color = vec4(texelFetch(intex, ¶ms));
|
||||
}
|
||||
)EOSHADER";
|
||||
|
||||
std::string pixelMSFloat = R"EOSHADER(
|
||||
#version 420 core
|
||||
|
||||
uniform uint texWidth;
|
||||
uniform uint slice;
|
||||
uniform uint mip;
|
||||
uniform uint flags;
|
||||
uniform uint zlayer;
|
||||
|
||||
float srgb2linear(float f)
|
||||
{
|
||||
if (f <= 0.04045f)
|
||||
return f / 12.92f;
|
||||
else
|
||||
return pow((f + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
|
||||
layout(location = 0, index = 0) out vec4 Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint x = uint(gl_FragCoord.x);
|
||||
uint y = uint(gl_FragCoord.y);
|
||||
|
||||
vec4 ret = vec4(0.1f, 0.35f, 0.6f, 0.85f);
|
||||
|
||||
// each 3D slice cycles the x. This only affects the primary diagonal
|
||||
uint offs_x = (x + zlayer) % max(1u, texWidth >> mip);
|
||||
|
||||
// pixels off the diagonal invert the colors
|
||||
if(offs_x != y)
|
||||
ret = ret.wzyx;
|
||||
|
||||
// second slice adds a coarse checkerboard pattern of inversion
|
||||
if(slice > 0 && (((x / 2) % 2) != ((y / 2) % 2)))
|
||||
ret = ret.wzyx;
|
||||
|
||||
// second sample/mip is shifted up a bit. MSAA textures have no mips,
|
||||
// textures with mips have no samples.
|
||||
ret += 0.075f.xxxx * (gl_SampleID + mip);
|
||||
|
||||
// Signed normals are negative
|
||||
if((flags & 1) != 0)
|
||||
ret = -ret;
|
||||
|
||||
// undo SRGB curve applied in output merger, to match the textures we just blat values into
|
||||
// without conversion (which are then interpreted as srgb implicitly)
|
||||
if((flags & 2) != 0)
|
||||
{
|
||||
ret.r = srgb2linear(ret.r);
|
||||
ret.g = srgb2linear(ret.g);
|
||||
ret.b = srgb2linear(ret.b);
|
||||
}
|
||||
|
||||
// BGR flip - same as above, for BGRA textures
|
||||
if((flags & 4) != 0)
|
||||
ret.rgb = ret.bgr;
|
||||
|
||||
// put red into alpha, because that's what we did in manual upload
|
||||
if((flags & 8) != 0)
|
||||
ret.a = ret.r;
|
||||
|
||||
Color = ret;
|
||||
}
|
||||
|
||||
)EOSHADER";
|
||||
|
||||
std::string pixelMSDepth = R"EOSHADER(
|
||||
#version 420 core
|
||||
|
||||
uniform uint texWidth;
|
||||
uniform uint slice;
|
||||
uniform uint mip;
|
||||
uniform uint flags;
|
||||
uniform uint zlayer;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint x = uint(gl_FragCoord.x);
|
||||
uint y = uint(gl_FragCoord.y);
|
||||
|
||||
float ret = 0.1f;
|
||||
|
||||
// each 3D slice cycles the x. This only affects the primary diagonal
|
||||
uint offs_x = (x + zlayer) % max(1u, texWidth >> mip);
|
||||
|
||||
// pixels off the diagonal invert the colors
|
||||
// second slice adds a coarse checkerboard pattern of inversion
|
||||
if((offs_x != y) != (slice > 0 && (((x / 2) % 2) != ((y / 2) % 2))))
|
||||
{
|
||||
ret = 0.85f;
|
||||
|
||||
// so we can fill stencil data, clip off the inverted values
|
||||
if(flags == 1)
|
||||
discard;
|
||||
}
|
||||
|
||||
// second sample/mip is shifted up a bit. MSAA textures have no mips,
|
||||
// textures with mips have no samples.
|
||||
ret += 0.075f * (gl_SampleID + mip);
|
||||
|
||||
gl_FragDepth = ret;
|
||||
}
|
||||
|
||||
)EOSHADER";
|
||||
|
||||
std::string pixelMSUInt = R"EOSHADER(
|
||||
#version 420 core
|
||||
|
||||
uniform uint texWidth;
|
||||
uniform uint slice;
|
||||
uniform uint mip;
|
||||
uniform uint flags;
|
||||
uniform uint zlayer;
|
||||
|
||||
layout(location = 0, index = 0) out uvec4 Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint x = uint(gl_FragCoord.x);
|
||||
uint y = uint(gl_FragCoord.y);
|
||||
|
||||
uvec4 ret = uvec4(10, 40, 70, 100);
|
||||
|
||||
// each 3D slice cycles the x. This only affects the primary diagonal
|
||||
uint offs_x = (x + zlayer) % max(1u, texWidth >> mip);
|
||||
|
||||
// pixels off the diagonal invert the colors
|
||||
if(offs_x != y)
|
||||
ret = ret.wzyx;
|
||||
|
||||
// second slice adds a coarse checkerboard pattern of inversion
|
||||
if(slice > 0 && (((x / 2) % 2) != ((y / 2) % 2)))
|
||||
ret = ret.wzyx;
|
||||
|
||||
// second sample/mip is shifted up a bit. MSAA textures have no mips,
|
||||
// textures with mips have no samples.
|
||||
ret += uvec4(10, 10, 10, 10) * (gl_SampleID + mip);
|
||||
|
||||
Color = ret;
|
||||
}
|
||||
|
||||
)EOSHADER";
|
||||
|
||||
std::string pixelMSSInt = R"EOSHADER(
|
||||
#version 420 core
|
||||
|
||||
uniform uint texWidth;
|
||||
uniform uint slice;
|
||||
uniform uint mip;
|
||||
uniform uint flags;
|
||||
uniform uint zlayer;
|
||||
|
||||
layout(location = 0, index = 0) out ivec4 Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint x = uint(gl_FragCoord.x);
|
||||
uint y = uint(gl_FragCoord.y);
|
||||
|
||||
ivec4 ret = ivec4(10, 40, 70, 100);
|
||||
|
||||
// each 3D slice cycles the x. This only affects the primary diagonal
|
||||
uint offs_x = (x + zlayer) % max(1u, texWidth >> mip);
|
||||
|
||||
// pixels off the diagonal invert the colors
|
||||
if(offs_x != y)
|
||||
ret = ret.wzyx;
|
||||
|
||||
// second slice adds a coarse checkerboard pattern of inversion
|
||||
if(slice > 0 && (((x / 2) % 2) != ((y / 2) % 2)))
|
||||
ret = ret.wzyx;
|
||||
|
||||
// second sample/mip is shifted up a bit. MSAA textures have no mips,
|
||||
// textures with mips have no samples.
|
||||
ret += ivec4(10 * (gl_SampleID + mip));
|
||||
|
||||
Color = -ret;
|
||||
}
|
||||
|
||||
)EOSHADER";
|
||||
|
||||
struct GLFormat
|
||||
{
|
||||
const std::string name;
|
||||
GLenum internalFormat;
|
||||
TexConfig cfg;
|
||||
};
|
||||
|
||||
struct TestCase
|
||||
{
|
||||
GLFormat fmt;
|
||||
GLenum target;
|
||||
uint32_t dim;
|
||||
bool isArray;
|
||||
bool isMSAA;
|
||||
bool isRect;
|
||||
bool canRender;
|
||||
bool canDepth;
|
||||
bool canStencil;
|
||||
bool hasData;
|
||||
GLuint tex;
|
||||
};
|
||||
|
||||
std::string MakeName(const TestCase &test)
|
||||
{
|
||||
std::string name = "Texture " + std::to_string(test.dim) + "D";
|
||||
|
||||
if(test.isRect)
|
||||
name = "Texture Rect";
|
||||
|
||||
if(test.isMSAA)
|
||||
name += " MSAA";
|
||||
if(test.isArray)
|
||||
name += " Array";
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
GLuint GetProgram(const TestCase &test)
|
||||
{
|
||||
static std::map<uint32_t, GLuint> programs;
|
||||
|
||||
uint32_t key = uint32_t(test.fmt.cfg.data);
|
||||
key |= test.dim << 6;
|
||||
key |= test.isMSAA ? 0x80000 : 0;
|
||||
key |= test.isArray ? 0x100000 : 0;
|
||||
key |= test.isRect ? 0x200000 : 0;
|
||||
|
||||
GLuint ret = programs[key];
|
||||
if(!ret)
|
||||
{
|
||||
std::string texType = "sampler" + std::to_string(test.dim) + "D";
|
||||
if(test.isMSAA)
|
||||
texType += "MS";
|
||||
if(test.isRect)
|
||||
texType += "Rect";
|
||||
if(test.dim < 3 && test.isArray)
|
||||
texType += "Array";
|
||||
|
||||
std::string typemod = "";
|
||||
|
||||
if(test.fmt.cfg.data == DataType::UInt)
|
||||
typemod = "u";
|
||||
else if(test.fmt.cfg.data == DataType::SInt)
|
||||
typemod = "i";
|
||||
|
||||
std::string src = pixelTemplate;
|
||||
|
||||
uint32_t dim = test.dim + (test.isArray ? 1 : 0);
|
||||
|
||||
if(test.isRect)
|
||||
src.replace(src.find("¶ms"), 7, "ivec2(0)");
|
||||
else if(dim == 1)
|
||||
src.replace(src.find("¶ms"), 7, "int(0), 0");
|
||||
else if(dim == 2)
|
||||
src.replace(src.find("¶ms"), 7, "ivec2(0), 0");
|
||||
else if(dim == 3)
|
||||
src.replace(src.find("¶ms"), 7, "ivec3(0), 0");
|
||||
|
||||
src.replace(src.find("&texdecl"), 8, typemod + texType);
|
||||
|
||||
ret = programs[key] = MakeProgram(blitVertex, src);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool QueryFormatBool(GLenum target, GLenum format, GLenum pname)
|
||||
{
|
||||
GLint param = 0;
|
||||
glGetInternalformativ(target, format, pname, 4, ¶m);
|
||||
return param != 0;
|
||||
}
|
||||
|
||||
void FinaliseTest(TestCase & test)
|
||||
{
|
||||
test.canRender = QueryFormatBool(test.target, test.fmt.internalFormat, GL_COLOR_RENDERABLE);
|
||||
test.canDepth = QueryFormatBool(test.target, test.fmt.internalFormat, GL_DEPTH_RENDERABLE);
|
||||
test.canStencil = QueryFormatBool(test.target, test.fmt.internalFormat, GL_STENCIL_RENDERABLE);
|
||||
|
||||
Vec4i dimensions(texWidth, texHeight, texDepth);
|
||||
|
||||
bool isCompressed =
|
||||
(test.fmt.cfg.type != TextureType::R9G9B9E5 && test.fmt.cfg.type != TextureType::Regular);
|
||||
|
||||
// Some GL drivers report that block compressed textures are supported for MSAA and color
|
||||
// rendering. Save them from themselves. Similarly they report support for 1D and 3D but then it
|
||||
// doesn't work properly
|
||||
if(isCompressed && (test.dim == 1 || test.dim == 3 || test.isRect || test.isMSAA))
|
||||
return;
|
||||
|
||||
// any format that supports MSAA but can't be rendered to is unsupported
|
||||
if(!test.canRender && !test.canDepth && !test.canStencil && test.isMSAA)
|
||||
return;
|
||||
|
||||
test.tex = MakeTexture();
|
||||
glBindTexture(test.target, test.tex);
|
||||
|
||||
if(test.dim == 1)
|
||||
{
|
||||
if(test.isArray)
|
||||
glTexStorage2D(test.target, texMips, test.fmt.internalFormat, texWidth, texSlices);
|
||||
else
|
||||
glTexStorage1D(test.target, texMips, test.fmt.internalFormat, texWidth);
|
||||
|
||||
dimensions.y = dimensions.z = 1;
|
||||
}
|
||||
else if(test.isRect)
|
||||
{
|
||||
glTexStorage2D(test.target, 1, test.fmt.internalFormat, texWidth, texHeight);
|
||||
|
||||
dimensions.z = 1;
|
||||
}
|
||||
else if(test.dim == 2)
|
||||
{
|
||||
if(test.isMSAA)
|
||||
{
|
||||
if(test.isArray)
|
||||
glTexStorage3DMultisample(test.target, texSamples, test.fmt.internalFormat, texWidth,
|
||||
texHeight, texSlices, GL_TRUE);
|
||||
else
|
||||
glTexStorage2DMultisample(test.target, texSamples, test.fmt.internalFormat, texWidth,
|
||||
texHeight, GL_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(test.isArray)
|
||||
glTexStorage3D(test.target, texMips, test.fmt.internalFormat, texWidth, texHeight,
|
||||
texSlices);
|
||||
else
|
||||
glTexStorage2D(test.target, texMips, test.fmt.internalFormat, texWidth, texHeight);
|
||||
}
|
||||
|
||||
dimensions.z = 1;
|
||||
}
|
||||
else if(test.dim == 3)
|
||||
{
|
||||
glTexStorage3D(test.target, texMips, test.fmt.internalFormat, texWidth, texHeight, texDepth);
|
||||
}
|
||||
|
||||
if(test.canRender || test.canDepth || test.canStencil)
|
||||
{
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0);
|
||||
|
||||
GLenum attach = GL_COLOR_ATTACHMENT0;
|
||||
if(test.canDepth && test.canStencil)
|
||||
attach = GL_DEPTH_STENCIL_ATTACHMENT;
|
||||
else if(test.canDepth)
|
||||
attach = GL_DEPTH_ATTACHMENT;
|
||||
else if(test.canStencil)
|
||||
attach = GL_STENCIL_ATTACHMENT;
|
||||
|
||||
if(test.dim == 3 || test.isArray)
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attach, test.tex, 0, 0);
|
||||
else
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attach, test.tex, 0);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
// sometimes the GL driver lies about being able to render to a format! weeee!
|
||||
if(status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
test.canRender = false;
|
||||
test.canDepth = false;
|
||||
test.canStencil = false;
|
||||
}
|
||||
|
||||
if(!test.canRender && !test.canDepth && !test.canStencil && test.isMSAA)
|
||||
{
|
||||
test.tex = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
glObjectLabel(GL_TEXTURE, test.tex, -1, (MakeName(test) + " " + test.fmt.name).c_str());
|
||||
|
||||
// invalidate the texture, this makes renderdoc treat it as dirty
|
||||
glInvalidateTexImage(test.tex, 0);
|
||||
|
||||
if(!test.isMSAA)
|
||||
{
|
||||
pushMarker("Set data for " + test.fmt.name + " " + MakeName(test));
|
||||
|
||||
test.hasData = SetData(test, dimensions);
|
||||
|
||||
popMarker();
|
||||
}
|
||||
}
|
||||
|
||||
bool SetData(const TestCase &test, Vec4i dim)
|
||||
{
|
||||
bool isCompressed =
|
||||
(test.fmt.cfg.type != TextureType::R9G9B9E5 && test.fmt.cfg.type != TextureType::Regular);
|
||||
|
||||
TexData data;
|
||||
|
||||
// tightly packed data
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
GLenum format = GL_RGBA;
|
||||
GLenum type = GL_UNSIGNED_BYTE;
|
||||
if(test.fmt.cfg.componentBytes == 2)
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
else if(test.fmt.cfg.componentBytes == 2)
|
||||
type = GL_UNSIGNED_INT;
|
||||
|
||||
bool isInt = (test.fmt.cfg.data == DataType::SInt || test.fmt.cfg.data == DataType::UInt);
|
||||
if(test.fmt.cfg.componentCount == 4)
|
||||
format = isInt ? GL_RGBA_INTEGER : GL_RGBA;
|
||||
else if(test.fmt.cfg.componentCount == 3)
|
||||
format = isInt ? GL_RGB_INTEGER : GL_RGB;
|
||||
else if(test.fmt.cfg.componentCount == 2)
|
||||
format = isInt ? GL_RG_INTEGER : GL_RG;
|
||||
else if(test.fmt.cfg.componentCount == 1)
|
||||
format = isInt ? GL_RED_INTEGER : GL_RED;
|
||||
|
||||
if(test.fmt.cfg.type == TextureType::R9G9B9E5)
|
||||
format = GL_RGB;
|
||||
|
||||
GLint slices = test.isArray ? texSlices : 1;
|
||||
GLint mips = test.isMSAA || test.isRect ? 1 : texMips;
|
||||
|
||||
for(GLint s = 0; s < slices; s++)
|
||||
{
|
||||
for(GLint m = 0; m < mips; m++)
|
||||
{
|
||||
MakeData(data, test.fmt.cfg, dim, m, s);
|
||||
|
||||
if(data.byteData.empty())
|
||||
return false;
|
||||
|
||||
uint32_t mipWidth = std::max(texWidth >> m, 1U);
|
||||
uint32_t mipHeight = std::max(texHeight >> m, 1U);
|
||||
uint32_t mipDepth = std::max(texDepth >> m, 1U);
|
||||
|
||||
if(isCompressed)
|
||||
{
|
||||
if(test.dim == 1)
|
||||
{
|
||||
if(test.isArray)
|
||||
glCompressedTexSubImage2D(test.target, m, 0, s, mipWidth, 1, test.fmt.internalFormat,
|
||||
(GLsizei)data.byteData.size(), data.byteData.data());
|
||||
else
|
||||
glCompressedTexSubImage1D(test.target, m, 0, mipWidth, format,
|
||||
(GLsizei)data.byteData.size(), data.byteData.data());
|
||||
}
|
||||
else if(test.isRect)
|
||||
{
|
||||
glCompressedTexSubImage2D(test.target, 0, 0, 0, mipWidth, mipHeight,
|
||||
test.fmt.internalFormat, (GLsizei)data.byteData.size(),
|
||||
data.byteData.data());
|
||||
}
|
||||
else if(test.dim == 2)
|
||||
{
|
||||
if(test.isArray)
|
||||
glCompressedTexSubImage3D(test.target, m, 0, 0, s, mipWidth, mipHeight, 1,
|
||||
test.fmt.internalFormat, (GLsizei)data.byteData.size(),
|
||||
data.byteData.data());
|
||||
else
|
||||
glCompressedTexSubImage2D(test.target, m, 0, 0, mipWidth, mipHeight,
|
||||
test.fmt.internalFormat, (GLsizei)data.byteData.size(),
|
||||
data.byteData.data());
|
||||
}
|
||||
else if(test.dim == 3)
|
||||
{
|
||||
glCompressedTexSubImage3D(test.target, m, 0, 0, 0, mipWidth, mipHeight, mipDepth,
|
||||
test.fmt.internalFormat, (GLsizei)data.byteData.size(),
|
||||
data.byteData.data());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(test.dim == 1)
|
||||
{
|
||||
if(test.isArray)
|
||||
glTexSubImage2D(test.target, m, 0, s, mipWidth, 1, format, type, data.byteData.data());
|
||||
else
|
||||
glTexSubImage1D(test.target, m, 0, mipWidth, format, type, data.byteData.data());
|
||||
}
|
||||
else if(test.isRect)
|
||||
{
|
||||
glTexSubImage2D(test.target, 0, 0, 0, mipWidth, mipHeight, format, type,
|
||||
data.byteData.data());
|
||||
}
|
||||
else if(test.dim == 2)
|
||||
{
|
||||
if(test.isArray)
|
||||
glTexSubImage3D(test.target, m, 0, 0, s, mipWidth, mipHeight, 1, format, type,
|
||||
data.byteData.data());
|
||||
else
|
||||
glTexSubImage2D(test.target, m, 0, 0, mipWidth, mipHeight, format, type,
|
||||
data.byteData.data());
|
||||
}
|
||||
else if(test.dim == 3)
|
||||
{
|
||||
glTexSubImage3D(test.target, m, 0, 0, 0, mipWidth, mipHeight, mipDepth, format, type,
|
||||
data.byteData.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddSupportedTests(const GLFormat &f, std::vector<TestCase> &test_textures, bool depthMode)
|
||||
{
|
||||
// TODO: disable 1D depth textures for now, we don't support displaying them
|
||||
if(!depthMode)
|
||||
{
|
||||
test_textures.push_back({f, GL_TEXTURE_1D, 1, false});
|
||||
test_textures.push_back({f, GL_TEXTURE_1D_ARRAY, 1, true});
|
||||
}
|
||||
|
||||
test_textures.push_back({f, GL_TEXTURE_2D, 2, false});
|
||||
test_textures.push_back({f, GL_TEXTURE_2D_ARRAY, 2, true});
|
||||
|
||||
test_textures.push_back({f, GL_TEXTURE_3D, 3, false});
|
||||
|
||||
// TODO: we don't support MSAA<->Array copies for these odd sized pixels, and I suspect drivers
|
||||
// emulate the formats anyway. Disable for now
|
||||
if(f.cfg.type != TextureType::Regular || f.cfg.componentCount != 3)
|
||||
{
|
||||
test_textures.push_back({f, GL_TEXTURE_2D_MULTISAMPLE, 2, false, true});
|
||||
test_textures.push_back({f, GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 2, true, true});
|
||||
}
|
||||
|
||||
test_textures.push_back({f, GL_TEXTURE_RECTANGLE, 2, false, false, true});
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// initialise, create window, create context, etc
|
||||
if(!Init())
|
||||
return 3;
|
||||
|
||||
GLuint vao = MakeVAO();
|
||||
glBindVertexArray(vao);
|
||||
|
||||
pushMarker("Add tests");
|
||||
|
||||
#define TEST_CASE_NAME(texFmt) #texFmt
|
||||
|
||||
#define TEST_CASE(texType, texFmt, compCount, byteWidth, dataType) \
|
||||
{ \
|
||||
#texFmt + 3, texFmt, {texType, compCount, byteWidth, dataType }, \
|
||||
}
|
||||
|
||||
std::vector<TestCase> test_textures;
|
||||
|
||||
const GLFormat color_tests[] = {
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA32F, 4, 4, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA32UI, 4, 4, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA32I, 4, 4, DataType::SInt),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RGB32F, 3, 4, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB32UI, 3, 4, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB32I, 3, 4, DataType::SInt),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RG32F, 2, 4, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_RG32UI, 2, 4, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RG32I, 2, 4, DataType::SInt),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_R32F, 1, 4, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_R32UI, 1, 4, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_R32I, 1, 4, DataType::SInt),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA16F, 4, 2, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA16UI, 4, 2, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA16I, 4, 2, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA16, 4, 2, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA16_SNORM, 4, 2, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RGB16F, 3, 2, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB16UI, 3, 2, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB16I, 3, 2, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB16, 3, 2, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB16_SNORM, 3, 2, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RG16F, 2, 2, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_RG16UI, 2, 2, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RG16I, 2, 2, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RG16, 2, 2, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_RG16_SNORM, 2, 2, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_R16F, 1, 2, DataType::Float),
|
||||
TEST_CASE(TextureType::Regular, GL_R16UI, 1, 2, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_R16I, 1, 2, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_R16, 1, 2, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_R16_SNORM, 1, 2, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA8UI, 4, 1, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA8I, 4, 1, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA8, 4, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_SRGB8_ALPHA8, 4, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_RGBA8_SNORM, 4, 1, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RGB8UI, 3, 1, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB8I, 3, 1, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB8, 3, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_SRGB8, 3, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_RGB8_SNORM, 3, 1, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_RG8UI, 2, 1, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RG8I, 2, 1, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_RG8, 2, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_SRG8_EXT, 1, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_RG8_SNORM, 2, 1, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Regular, GL_R8UI, 1, 1, DataType::UInt),
|
||||
TEST_CASE(TextureType::Regular, GL_R8I, 1, 1, DataType::SInt),
|
||||
TEST_CASE(TextureType::Regular, GL_R8, 1, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_SR8_EXT, 1, 1, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Regular, GL_R8_SNORM, 1, 1, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::Unknown, GL_RGB565, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Unknown, GL_RGB5_A1, 0, 0, DataType::UNorm),
|
||||
|
||||
TEST_CASE(TextureType::Unknown, GL_RGB10_A2, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::Unknown, GL_RGB10_A2UI, 0, 0, DataType::UInt),
|
||||
|
||||
TEST_CASE(TextureType::Unknown, GL_RGBA4, 0, 0, DataType::UNorm),
|
||||
|
||||
// formats we don't support in RenderDoc currently
|
||||
// TEST_CASE(TextureType::Unknown, GL_RGB4, 0, 0, DataType::UNorm),
|
||||
// TEST_CASE(TextureType::Unknown, GL_RGB5, 0, 0, DataType::UNorm),
|
||||
// TEST_CASE(TextureType::Unknown, GL_RGB10, 0, 0, DataType::UNorm),
|
||||
|
||||
TEST_CASE(TextureType::Unknown, GL_R11F_G11F_B10F, 0, 0, DataType::UNorm),
|
||||
|
||||
TEST_CASE(TextureType::R9G9B9E5, GL_RGB9_E5, 0, 0, DataType::Float),
|
||||
|
||||
TEST_CASE(TextureType::BC1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC1, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC1, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC1, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, 0, 0, DataType::UNorm),
|
||||
|
||||
TEST_CASE(TextureType::BC2, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC2, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0, 0, DataType::UNorm),
|
||||
|
||||
TEST_CASE(TextureType::BC3, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC3, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0, 0, DataType::UNorm),
|
||||
|
||||
TEST_CASE(TextureType::BC4, GL_COMPRESSED_RED_RGTC1_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC4, GL_COMPRESSED_SIGNED_RED_RGTC1_EXT, 0, 0, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::BC5, GL_COMPRESSED_RED_GREEN_RGTC2_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC5, GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, 0, 0, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::BC6, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC6, GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, 0, 0, DataType::SNorm),
|
||||
|
||||
TEST_CASE(TextureType::BC7, GL_COMPRESSED_RGBA_BPTC_UNORM, 0, 0, DataType::UNorm),
|
||||
TEST_CASE(TextureType::BC7, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, 0, 0, DataType::UNorm),
|
||||
};
|
||||
|
||||
for(GLFormat f : color_tests)
|
||||
{
|
||||
if(f.internalFormat == GL_SR8_EXT && !GLAD_GL_EXT_texture_sRGB_R8)
|
||||
continue;
|
||||
|
||||
if(f.internalFormat == GL_SRG8_EXT && !GLAD_GL_EXT_texture_sRGB_RG8)
|
||||
continue;
|
||||
|
||||
AddSupportedTests(f, test_textures, false);
|
||||
}
|
||||
|
||||
// finally add the depth tests
|
||||
const GLFormat depth_tests[] = {
|
||||
TEST_CASE(TextureType::Unknown, GL_DEPTH32F_STENCIL8, 0, 0, DataType::Float),
|
||||
TEST_CASE(TextureType::Unknown, GL_DEPTH_COMPONENT32F, 0, 0, DataType::Float),
|
||||
|
||||
TEST_CASE(TextureType::Unknown, GL_DEPTH24_STENCIL8, 0, 0, DataType::Float),
|
||||
TEST_CASE(TextureType::Unknown, GL_DEPTH_COMPONENT24, 0, 0, DataType::Float),
|
||||
|
||||
TEST_CASE(TextureType::Unknown, GL_DEPTH_COMPONENT16, 0, 0, DataType::Float),
|
||||
};
|
||||
|
||||
for(GLFormat f : depth_tests)
|
||||
AddSupportedTests(f, test_textures, true);
|
||||
|
||||
GLuint renderFBO = MakeFBO();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
|
||||
|
||||
for(TestCase &t : test_textures)
|
||||
{
|
||||
if(QueryFormatBool(t.target, t.fmt.internalFormat, GL_INTERNALFORMAT_SUPPORTED) &&
|
||||
QueryFormatBool(t.target, t.fmt.internalFormat, GL_FRAGMENT_TEXTURE))
|
||||
{
|
||||
FinaliseTest(t);
|
||||
}
|
||||
}
|
||||
|
||||
popMarker();
|
||||
|
||||
GLuint msprog[(size_t)DataType::Count];
|
||||
|
||||
msprog[(size_t)DataType::Float] = msprog[(size_t)DataType::UNorm] =
|
||||
msprog[(size_t)DataType::SNorm] = MakeProgram(blitVertex, pixelMSFloat);
|
||||
msprog[(size_t)DataType::UInt] = MakeProgram(blitVertex, pixelMSUInt);
|
||||
msprog[(size_t)DataType::SInt] = MakeProgram(blitVertex, pixelMSSInt);
|
||||
|
||||
GLuint msdepthprog = MakeProgram(blitVertex, pixelMSDepth);
|
||||
|
||||
for(TestCase &t : test_textures)
|
||||
{
|
||||
if(!t.tex || t.hasData)
|
||||
continue;
|
||||
|
||||
if(!t.canRender && !t.canDepth && !t.canStencil)
|
||||
{
|
||||
TEST_ERROR("Need data for test %s, but it's not a renderable/depthable format",
|
||||
t.fmt.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0);
|
||||
|
||||
if(t.canDepth || t.canStencil)
|
||||
{
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glDepthMask(0xff);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glStencilFunc(GL_ALWAYS, 0, 0xff);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
pushMarker("Render data for " + t.fmt.name + " " + MakeName(t));
|
||||
|
||||
t.hasData = true;
|
||||
|
||||
bool srgb = false;
|
||||
switch(t.fmt.internalFormat)
|
||||
{
|
||||
// only need to handle renderable SRGB formats here
|
||||
case GL_SRGB8:
|
||||
case GL_SRGB8_ALPHA8: srgb = true; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if(t.fmt.cfg.data == DataType::SNorm)
|
||||
flags |= 1;
|
||||
if(srgb)
|
||||
flags |= 2;
|
||||
|
||||
GLuint slices = t.isArray ? texSlices : 1u;
|
||||
GLuint mips = t.isMSAA || t.isRect ? 1u : texMips;
|
||||
|
||||
for(GLuint mp = 0; mp < mips; mp++)
|
||||
{
|
||||
GLuint SlicesOrDepth = slices;
|
||||
if(t.dim == 3)
|
||||
SlicesOrDepth >>= mp;
|
||||
|
||||
for(GLuint sl = 0; sl < SlicesOrDepth; sl++)
|
||||
{
|
||||
if(t.canDepth || t.canStencil)
|
||||
{
|
||||
GLenum attach = GL_NONE;
|
||||
if(t.canDepth && t.canStencil)
|
||||
attach = GL_DEPTH_STENCIL_ATTACHMENT;
|
||||
else if(t.canDepth)
|
||||
attach = GL_DEPTH_ATTACHMENT;
|
||||
else if(t.canStencil)
|
||||
attach = GL_STENCIL_ATTACHMENT;
|
||||
|
||||
if(t.dim == 3 || t.isArray)
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attach, t.tex, mp, sl);
|
||||
else
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attach, t.tex, mp);
|
||||
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, 0.0, 0);
|
||||
|
||||
GLuint p = msdepthprog;
|
||||
|
||||
glUseProgram(p);
|
||||
|
||||
glUniform1ui(glGetUniformLocation(p, "texWidth"), texWidth);
|
||||
|
||||
glUniform1ui(glGetUniformLocation(p, "slice"), sl);
|
||||
glUniform1ui(glGetUniformLocation(p, "mip"), mp);
|
||||
glUniform1ui(glGetUniformLocation(p, "flags"), flags);
|
||||
glUniform1ui(glGetUniformLocation(p, "zlayer"), t.dim == 3 ? sl : 0);
|
||||
|
||||
glViewport(0, 0, texWidth, texHeight);
|
||||
|
||||
uint32_t sampleCount = t.isMSAA ? texSamples : 1;
|
||||
|
||||
// need to do each sample separately to let us vary the stencil value
|
||||
for(uint32_t sm = 0; sm < sampleCount; sm++)
|
||||
{
|
||||
glSampleMaski(0, 1 << sm);
|
||||
|
||||
glStencilFunc(GL_ALWAYS, 100 + (mp + sm) * 10, 0xff);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
// clip off the diagonal
|
||||
glUniform1ui(glGetUniformLocation(p, "flags"), 1);
|
||||
|
||||
glStencilFunc(GL_ALWAYS, 10 + (mp + sm) * 10, 0xff);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(t.dim == 3 || t.isArray)
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, t.tex, mp, sl);
|
||||
else
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, t.tex, mp);
|
||||
|
||||
GLuint p = msprog[(size_t)t.fmt.cfg.data];
|
||||
|
||||
glUseProgram(p);
|
||||
|
||||
glUniform1ui(glGetUniformLocation(p, "texWidth"), texWidth);
|
||||
|
||||
glUniform1ui(glGetUniformLocation(p, "slice"), t.dim == 3 ? 0 : sl);
|
||||
glUniform1ui(glGetUniformLocation(p, "mip"), mp);
|
||||
glUniform1ui(glGetUniformLocation(p, "flags"), flags);
|
||||
glUniform1ui(glGetUniformLocation(p, "zlayer"), t.dim == 3 ? sl : 0);
|
||||
|
||||
glViewport(0, 0, texWidth, texHeight);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popMarker();
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
GLuint fbo = MakeFBO();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
// Color render texture
|
||||
GLuint colattach = MakeTexture();
|
||||
glBindTexture(GL_TEXTURE_2D, colattach);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, screenWidth, screenHeight);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colattach, 0);
|
||||
|
||||
while(Running())
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
float col[] = {0.4f, 0.5f, 0.6f, 1.0f};
|
||||
glClearBufferfv(GL_COLOR, 0, col);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
GLsizei viewX = 0, viewY = screenHeight - 10;
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
for(size_t i = 0; i < test_textures.size(); i++)
|
||||
{
|
||||
if(i == 0 || test_textures[i].fmt.internalFormat != test_textures[i - 1].fmt.internalFormat)
|
||||
{
|
||||
if(i != 0)
|
||||
popMarker();
|
||||
|
||||
pushMarker(test_textures[i].fmt.name);
|
||||
}
|
||||
|
||||
setMarker(MakeName(test_textures[i]));
|
||||
|
||||
glViewport(viewX, viewY, 10, 10);
|
||||
glScissor(viewX + 1, viewY + 1, 8, 8);
|
||||
|
||||
glUseProgram(GetProgram(test_textures[i]));
|
||||
|
||||
if(test_textures[i].tex)
|
||||
{
|
||||
glBindTextureUnit(0, test_textures[i].tex);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
setMarker("UNSUPPORTED");
|
||||
}
|
||||
|
||||
// advance to next viewport
|
||||
viewX += 10;
|
||||
if(viewX + 10 > (float)screenWidth)
|
||||
{
|
||||
viewX = 0;
|
||||
viewY -= 10;
|
||||
}
|
||||
}
|
||||
|
||||
// pop the last format region
|
||||
popMarker();
|
||||
|
||||
glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight));
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// blit to the screen for a nicer preview
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
Present();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST();
|
||||
@@ -492,4 +492,4 @@ bool GraphicsTest::FrameLimit()
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,8 @@ struct Vec4f
|
||||
z = Z;
|
||||
w = W;
|
||||
}
|
||||
bool operator==(const Vec4f &o) { return x == o.x && y == o.y && z == o.z && w == o.w; }
|
||||
bool operator!=(const Vec4f &o) { return !(*this == o); }
|
||||
float x, y, z, w;
|
||||
};
|
||||
|
||||
@@ -333,3 +335,62 @@ void DebugPrint(const char *fmt, ...);
|
||||
DEBUG_BREAK(); \
|
||||
exit(0); \
|
||||
} while(0)
|
||||
|
||||
namespace TextureZoo
|
||||
{
|
||||
enum class DataType
|
||||
{
|
||||
Float,
|
||||
UNorm,
|
||||
SNorm,
|
||||
UInt,
|
||||
SInt,
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class TextureType
|
||||
{
|
||||
Unknown,
|
||||
Regular,
|
||||
R9G9B9E5,
|
||||
G4R4,
|
||||
A4R4G4B4,
|
||||
R4G4B4A4,
|
||||
R5G6B5,
|
||||
R5G5B5A1,
|
||||
A1R5G5B5,
|
||||
RGB10A2,
|
||||
BC1,
|
||||
BC2,
|
||||
BC3,
|
||||
BC4,
|
||||
BC5,
|
||||
BC6,
|
||||
BC7,
|
||||
};
|
||||
|
||||
static const uint32_t texWidth = 8;
|
||||
static const uint32_t texHeight = 8;
|
||||
static const uint32_t texDepth = 10;
|
||||
static const uint32_t texMips = 3;
|
||||
static const uint32_t texSlices = 2;
|
||||
static const uint32_t texSamples = 2;
|
||||
|
||||
struct TexConfig
|
||||
{
|
||||
TextureType type;
|
||||
uint32_t componentCount;
|
||||
uint32_t componentBytes;
|
||||
DataType data;
|
||||
};
|
||||
|
||||
struct TexData
|
||||
{
|
||||
std::vector<byte> byteData;
|
||||
uint32_t rowPitch = 0;
|
||||
uint32_t slicePitch = 0;
|
||||
};
|
||||
|
||||
void MakeData(TexData &data, const TexConfig &cfg, Vec4i dimensions, uint32_t mip, uint32_t slice);
|
||||
|
||||
}; // namespace TextureZoo
|
||||
@@ -0,0 +1,820 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include <algorithm>
|
||||
#include "test_common.h"
|
||||
|
||||
namespace TextureZoo
|
||||
{
|
||||
// quick and dirty half conversion, doesn't handle NaN/inf/subnormal/truncation/rounding
|
||||
uint16_t MakeHalf(float f)
|
||||
{
|
||||
bool sign = f < 0.0f;
|
||||
f = sign ? -f : f;
|
||||
|
||||
if(f < 1e-15f)
|
||||
return 0;
|
||||
|
||||
int exp;
|
||||
f = frexpf(f, &exp);
|
||||
|
||||
uint32_t mantissa;
|
||||
memcpy(&mantissa, &f, sizeof(mantissa));
|
||||
mantissa = (mantissa & 0x007fffff) >> 13;
|
||||
|
||||
uint16_t ret = mantissa & 0x3ff;
|
||||
ret |= ((exp + 14) << 10);
|
||||
if(sign)
|
||||
ret |= 0x8000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MakePixel(byte *data, const TexConfig &cfg, uint32_t x, uint32_t y, uint32_t z, uint32_t mip,
|
||||
uint32_t slice)
|
||||
{
|
||||
// each 3D slice cycles the x
|
||||
x += z;
|
||||
x %= std::max(1U, texWidth >> mip);
|
||||
|
||||
if(cfg.data == DataType::Float || cfg.data == DataType::UNorm || cfg.data == DataType::SNorm)
|
||||
{
|
||||
// start points for each component
|
||||
const float vals[] = {
|
||||
0.1f, 0.35f, 0.6f, 0.85f,
|
||||
};
|
||||
|
||||
for(uint32_t c = 0; c < cfg.componentCount; c++)
|
||||
{
|
||||
uint32_t idx = c;
|
||||
|
||||
// pixels off the diagonal invert the colors
|
||||
if(x != y)
|
||||
idx = 3 - idx;
|
||||
|
||||
// subsequent slices add a coarse checkerboard pattern of inverted colors
|
||||
if((slice % 3 > 0) && (((x / 2) % 2) != ((y / 2) % 2)))
|
||||
idx = 3 - idx;
|
||||
|
||||
float f = vals[idx];
|
||||
|
||||
// subsequent mips are shifted up a bit
|
||||
f += 0.075f * mip;
|
||||
|
||||
// Signed normals are negative
|
||||
if(cfg.data == DataType::SNorm)
|
||||
f = -f;
|
||||
|
||||
// if it's a full float, just copy
|
||||
if(cfg.componentBytes == 4)
|
||||
{
|
||||
memcpy(data, &f, cfg.componentBytes);
|
||||
}
|
||||
else if(cfg.componentBytes == 2)
|
||||
{
|
||||
uint16_t h;
|
||||
if(cfg.data == DataType::Float)
|
||||
h = MakeHalf(f);
|
||||
else if(cfg.data == DataType::UNorm)
|
||||
h = uint16_t(f * 0xffff);
|
||||
else if(cfg.data == DataType::SNorm)
|
||||
h = int16_t(f * 0x7fff);
|
||||
memcpy(data, &h, cfg.componentBytes);
|
||||
}
|
||||
else if(cfg.componentBytes == 1)
|
||||
{
|
||||
uint8_t b;
|
||||
if(cfg.data == DataType::UNorm)
|
||||
b = uint8_t(f * 0xff);
|
||||
else if(cfg.data == DataType::SNorm)
|
||||
b = int8_t(f * 0x7f);
|
||||
memcpy(data, &b, cfg.componentBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_ERROR("Unexpected component bytes %d in float", cfg.componentBytes);
|
||||
}
|
||||
|
||||
data += cfg.componentBytes;
|
||||
}
|
||||
}
|
||||
else if(cfg.data == DataType::UInt || cfg.data == DataType::SInt)
|
||||
{
|
||||
// same pattern as above but with integer values
|
||||
const int32_t vals[] = {
|
||||
10, 40, 70, 100,
|
||||
};
|
||||
|
||||
for(uint32_t c = 0; c < cfg.componentCount; c++)
|
||||
{
|
||||
uint32_t idx = c;
|
||||
|
||||
// pixels off the diagonal invert the colors
|
||||
if(x != y)
|
||||
idx = 3 - idx;
|
||||
|
||||
if((slice % 3 > 0) && (((x / 2) % 2) != ((y / 2) % 2)))
|
||||
idx = 3 - idx;
|
||||
|
||||
int32_t val = vals[idx];
|
||||
|
||||
val += 10 * mip;
|
||||
|
||||
// Signed ints are negative
|
||||
if(cfg.data == DataType::SInt)
|
||||
val = -val;
|
||||
|
||||
// because the values are below one byte and we're little-endian we can just copy the
|
||||
// right number of bytes from val
|
||||
memcpy(data, &val, cfg.componentBytes);
|
||||
|
||||
data += cfg.componentBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MakeData(TexData &data, const TexConfig &cfg, Vec4i dimensions, uint32_t mip, uint32_t slice)
|
||||
{
|
||||
uint32_t mipWidth = std::max(1, dimensions.x >> mip);
|
||||
uint32_t mipHeight = std::max(1, dimensions.y >> mip);
|
||||
uint32_t mipDepth = std::max(1, dimensions.z >> mip);
|
||||
|
||||
if(cfg.type == TextureType::Unknown)
|
||||
{
|
||||
data = TexData();
|
||||
return;
|
||||
}
|
||||
else if(cfg.type == TextureType::Regular)
|
||||
{
|
||||
uint32_t pixelPitch = cfg.componentBytes * cfg.componentCount;
|
||||
data.rowPitch = pixelPitch * mipWidth;
|
||||
data.slicePitch = data.rowPitch * mipHeight;
|
||||
|
||||
data.byteData.resize(data.slicePitch * mipDepth);
|
||||
|
||||
byte *out = data.byteData.data();
|
||||
|
||||
for(uint32_t z = 0; z < mipDepth; z++)
|
||||
{
|
||||
for(uint32_t y = 0; y < mipHeight; y++)
|
||||
{
|
||||
for(uint32_t x = 0; x < mipWidth; x++)
|
||||
{
|
||||
MakePixel(out, cfg, x, y, z, mip, slice);
|
||||
out += pixelPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool bc1 = false, bc2alpha = false, bc3alpha = false, bc6 = false, bc7 = false, sharedExp = false;
|
||||
int bc4channels = 0;
|
||||
uint32_t nybblePattern = 0;
|
||||
bool rgb5 = false;
|
||||
int alphabitPlace = 0;
|
||||
bool rgb10a2 = false;
|
||||
|
||||
switch(cfg.type)
|
||||
{
|
||||
case TextureType::BC1: bc1 = true; break;
|
||||
case TextureType::BC2:
|
||||
bc1 = true;
|
||||
bc2alpha = true;
|
||||
break;
|
||||
case TextureType::BC3:
|
||||
bc1 = true;
|
||||
bc3alpha = true;
|
||||
break;
|
||||
case TextureType::BC4: bc4channels = 1; break;
|
||||
case TextureType::BC5: bc4channels = 2; break;
|
||||
case TextureType::BC6: bc6 = true; break;
|
||||
case TextureType::BC7: bc7 = true; break;
|
||||
case TextureType::R9G9B9E5: sharedExp = true; break;
|
||||
case TextureType::G4R4: nybblePattern = 0x12; break;
|
||||
case TextureType::A4R4G4B4: nybblePattern = 0x1234; break;
|
||||
case TextureType::R4G4B4A4: nybblePattern = 0x4321; break;
|
||||
case TextureType::R5G6B5:
|
||||
rgb5 = true;
|
||||
alphabitPlace = 0;
|
||||
break;
|
||||
case TextureType::R5G5B5A1:
|
||||
rgb5 = true;
|
||||
alphabitPlace = 1;
|
||||
break;
|
||||
case TextureType::A1R5G5B5:
|
||||
rgb5 = true;
|
||||
alphabitPlace = 2;
|
||||
break;
|
||||
case TextureType::RGB10A2: rgb10a2 = true; break;
|
||||
default: data = TexData(); return;
|
||||
}
|
||||
|
||||
// get float data so we can do the best possible job of truncating to the desired bit width
|
||||
TexConfig floatcfg = {TextureType::Regular, 4, 4, DataType::Float};
|
||||
TexData floatdata;
|
||||
|
||||
if(rgb10a2 && cfg.data == DataType::UInt)
|
||||
floatcfg.data = cfg.data;
|
||||
|
||||
MakeData(floatdata, floatcfg, dimensions, mip, slice);
|
||||
|
||||
Vec4f *srcPixels = (Vec4f *)floatdata.byteData.data();
|
||||
Vec4i *srcPixelsI = (Vec4i *)floatdata.byteData.data();
|
||||
|
||||
if(rgb10a2)
|
||||
{
|
||||
uint32_t pixelPitch = 4;
|
||||
data.rowPitch = pixelPitch * mipWidth;
|
||||
data.slicePitch = data.rowPitch * mipHeight;
|
||||
|
||||
data.byteData.resize(data.slicePitch * mipDepth);
|
||||
|
||||
uint32_t *out = (uint32_t *)data.byteData.data();
|
||||
for(uint32_t z = 0; z < mipDepth; z++)
|
||||
{
|
||||
for(uint32_t y = 0; y < mipHeight; y++)
|
||||
{
|
||||
for(uint32_t x = 0; x < mipWidth; x++)
|
||||
{
|
||||
uint32_t encodedPixel = 0;
|
||||
|
||||
if(cfg.data == DataType::UInt)
|
||||
{
|
||||
int32_t rgba[4];
|
||||
rgba[0] = srcPixelsI[y * mipWidth + x].x;
|
||||
rgba[1] = srcPixelsI[y * mipWidth + x].y;
|
||||
rgba[2] = srcPixelsI[y * mipWidth + x].z;
|
||||
rgba[3] = srcPixelsI[y * mipWidth + x].w;
|
||||
|
||||
encodedPixel |= (rgba[0] & 0x3ff) << 0;
|
||||
encodedPixel |= (rgba[1] & 0x3ff) << 10;
|
||||
encodedPixel |= (rgba[2] & 0x3ff) << 20;
|
||||
encodedPixel |= (std::min(rgba[3], 3) & 0x3) << 30;
|
||||
}
|
||||
else
|
||||
{
|
||||
float rgba[4];
|
||||
rgba[0] = srcPixels[y * mipWidth + x].x;
|
||||
rgba[1] = srcPixels[y * mipWidth + x].y;
|
||||
rgba[2] = srcPixels[y * mipWidth + x].z;
|
||||
rgba[3] = srcPixels[y * mipWidth + x].w;
|
||||
|
||||
encodedPixel |= uint32_t(round(rgba[0] * 0x3ff)) << 0;
|
||||
encodedPixel |= uint32_t(round(rgba[1] * 0x3ff)) << 10;
|
||||
encodedPixel |= uint32_t(round(rgba[2] * 0x3ff)) << 20;
|
||||
encodedPixel |= uint32_t(round(rgba[3] * 0x3)) << 30;
|
||||
}
|
||||
|
||||
*out = encodedPixel;
|
||||
out++;
|
||||
}
|
||||
}
|
||||
|
||||
srcPixels += mipWidth * mipHeight;
|
||||
srcPixelsI += mipWidth * mipHeight;
|
||||
}
|
||||
}
|
||||
else if(nybblePattern || rgb5)
|
||||
{
|
||||
uint32_t pixelPitch = 2;
|
||||
data.rowPitch = pixelPitch * mipWidth;
|
||||
data.slicePitch = data.rowPitch * mipHeight;
|
||||
|
||||
data.byteData.resize(data.slicePitch * mipDepth);
|
||||
|
||||
uint8_t *out = data.byteData.data();
|
||||
|
||||
for(uint32_t z = 0; z < mipDepth; z++)
|
||||
{
|
||||
for(uint32_t y = 0; y < mipHeight; y++)
|
||||
{
|
||||
for(uint32_t x = 0; x < mipWidth; x++)
|
||||
{
|
||||
float rgb[4];
|
||||
rgb[0] = srcPixels[y * mipWidth + x].x;
|
||||
rgb[1] = srcPixels[y * mipWidth + x].y;
|
||||
rgb[2] = srcPixels[y * mipWidth + x].z;
|
||||
rgb[3] = srcPixels[y * mipWidth + x].w;
|
||||
|
||||
if(rgb5)
|
||||
{
|
||||
bool alpha = rgb[3] >= 0.5f;
|
||||
|
||||
uint16_t encodedPixel = 0;
|
||||
|
||||
if(alphabitPlace == 0)
|
||||
{
|
||||
encodedPixel |= uint16_t(rgb[2] * 31) << 0;
|
||||
encodedPixel |= uint16_t(rgb[1] * 63) << 5;
|
||||
encodedPixel |= uint16_t(rgb[0] * 31) << 11;
|
||||
}
|
||||
else
|
||||
{
|
||||
encodedPixel |= uint16_t(rgb[2] * 31) << 0;
|
||||
encodedPixel |= uint16_t(rgb[1] * 31) << 5;
|
||||
encodedPixel |= uint16_t(rgb[0] * 31) << 10;
|
||||
|
||||
if(alphabitPlace == 1)
|
||||
{
|
||||
if(alpha)
|
||||
encodedPixel |= 0x8000;
|
||||
}
|
||||
else
|
||||
{
|
||||
encodedPixel <<= 1;
|
||||
if(alpha)
|
||||
encodedPixel |= 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(out, &encodedPixel, sizeof(encodedPixel));
|
||||
out += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t encodedPixel = 0;
|
||||
|
||||
encodedPixel |= uint8_t(rgb[((nybblePattern & 0x000f) >> 0) - 1] * 15) << 0;
|
||||
encodedPixel |= uint8_t(rgb[((nybblePattern & 0x00f0) >> 4) - 1] * 15) << 4;
|
||||
|
||||
*out = encodedPixel;
|
||||
out++;
|
||||
|
||||
if(nybblePattern & 0xff00)
|
||||
{
|
||||
encodedPixel = 0;
|
||||
encodedPixel |= uint8_t(rgb[((nybblePattern & 0x0f00) >> 8) - 1] * 15) << 0;
|
||||
encodedPixel |= uint8_t(rgb[((nybblePattern & 0xf000) >> 12) - 1] * 15) << 4;
|
||||
|
||||
*out = encodedPixel;
|
||||
out++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcPixels += mipWidth * mipHeight;
|
||||
}
|
||||
}
|
||||
else if(sharedExp)
|
||||
{
|
||||
uint32_t pixelPitch = 4;
|
||||
data.rowPitch = pixelPitch * mipWidth;
|
||||
data.slicePitch = data.rowPitch * mipHeight;
|
||||
|
||||
data.byteData.resize(data.slicePitch * mipDepth);
|
||||
|
||||
uint32_t *out = (uint32_t *)data.byteData.data();
|
||||
|
||||
for(uint32_t z = 0; z < mipDepth; z++)
|
||||
{
|
||||
for(uint32_t y = 0; y < mipHeight; y++)
|
||||
{
|
||||
for(uint32_t x = 0; x < mipWidth; x++)
|
||||
{
|
||||
float rgb[3];
|
||||
rgb[0] = srcPixels[y * mipWidth + x].x;
|
||||
rgb[1] = srcPixels[y * mipWidth + x].y;
|
||||
rgb[2] = srcPixels[y * mipWidth + x].z;
|
||||
|
||||
uint32_t encodedPixel = 0;
|
||||
|
||||
int exp = -10;
|
||||
// we pick the highest exponent, losing bits off the bottom of any value that
|
||||
// needs a lower one, rather than picking a lower one and having to saturate
|
||||
// values that need a higher one
|
||||
for(int channel = 0; channel < 3; channel++)
|
||||
{
|
||||
int e = 0;
|
||||
frexpf(rgb[channel], &e);
|
||||
exp = std::max(exp, e);
|
||||
}
|
||||
|
||||
for(int channel = 0; channel < 3; channel++)
|
||||
encodedPixel |= uint32_t(rgb[channel] * 511.0 / (1 << exp)) << (9 * channel);
|
||||
|
||||
encodedPixel |= (exp + 15) << 27;
|
||||
|
||||
*out = encodedPixel;
|
||||
out++;
|
||||
}
|
||||
}
|
||||
|
||||
srcPixels += mipWidth * mipHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// these don't change, but make the code easier to read
|
||||
const uint32_t blockWidth = 4;
|
||||
const uint32_t blockHeight = 4;
|
||||
|
||||
uint32_t blockSize;
|
||||
|
||||
// 0.5 byte per pixel
|
||||
if(cfg.type == TextureType::BC1 || cfg.type == TextureType::BC4)
|
||||
blockSize = 8;
|
||||
else
|
||||
blockSize = 16;
|
||||
|
||||
data.rowPitch = blockSize * std::max(1U, mipWidth / blockWidth);
|
||||
data.slicePitch = data.rowPitch * std::max(1U, mipHeight / blockHeight);
|
||||
|
||||
data.byteData.resize(data.slicePitch * mipDepth);
|
||||
|
||||
byte *out = (byte *)data.byteData.data();
|
||||
|
||||
const Vec4f invalid(999001.0f, 999002.0f, -999003.0f, -999004.0f);
|
||||
|
||||
// compress each slice separately
|
||||
for(uint32_t z = 0; z < mipDepth; z++)
|
||||
{
|
||||
// block compressed - iterate over the pixels in block size
|
||||
for(uint32_t y = 0; y < mipHeight; y += blockHeight)
|
||||
{
|
||||
for(uint32_t x = 0; x < mipWidth; x += blockWidth)
|
||||
{
|
||||
Vec4f blockPixels[blockWidth * blockHeight] = {};
|
||||
|
||||
// copy all the in-range pixels into the block data
|
||||
for(uint32_t by = 0; by < blockHeight; by++)
|
||||
{
|
||||
for(uint32_t bx = 0; bx < blockWidth; bx++)
|
||||
{
|
||||
if(x + bx >= mipWidth || y + by >= mipHeight)
|
||||
{
|
||||
blockPixels[by * blockWidth + bx] = invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPixels[by * blockWidth + bx] = srcPixels[(y + by) * mipWidth + (x + bx)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we should have at most two unique pixels. The pattern is structured to allow
|
||||
// that, since any other colour can't be uniquely represented in all compressed
|
||||
// formats (even interpolated values)
|
||||
Vec4f a = invalid, b = invalid;
|
||||
uint32_t bc1bitmask = 0;
|
||||
uint64_t bc4bitmask = 0;
|
||||
|
||||
// BC1 and BC4 both share A = 0 and B = 0 codes
|
||||
enum class BCCode : uint64_t
|
||||
{
|
||||
A = 0,
|
||||
B = 1,
|
||||
};
|
||||
|
||||
// iterate the pixels in the block in ascending bitmask order
|
||||
for(uint32_t p = 0; p < blockWidth * blockHeight; p++)
|
||||
{
|
||||
if(blockPixels[p] == invalid)
|
||||
{
|
||||
// out of bounds pixel (think of a 2x2 mip), store as A - whatever A is.
|
||||
bc1bitmask |= uint32_t(BCCode::A) << (p * 2);
|
||||
bc4bitmask |= uint64_t(BCCode::A) << (p * 3);
|
||||
}
|
||||
else if(a == invalid)
|
||||
{
|
||||
// A hasn't been found yet, let's use this pixel for that
|
||||
a = blockPixels[p];
|
||||
bc1bitmask |= uint32_t(BCCode::A) << (p * 2);
|
||||
bc4bitmask |= uint64_t(BCCode::A) << (p * 3);
|
||||
}
|
||||
else if(blockPixels[p] == a)
|
||||
{
|
||||
// if A has been found then re-use it before assigning to B
|
||||
bc1bitmask |= uint32_t(BCCode::A) << (p * 2);
|
||||
bc4bitmask |= uint64_t(BCCode::A) << (p * 3);
|
||||
}
|
||||
else if(b == invalid)
|
||||
{
|
||||
// B hasn't been found yet, let's use this pixel for that
|
||||
b = blockPixels[p];
|
||||
bc1bitmask |= uint32_t(BCCode::B) << (p * 2);
|
||||
bc4bitmask |= uint64_t(BCCode::B) << (p * 3);
|
||||
}
|
||||
else if(blockPixels[p] == b)
|
||||
{
|
||||
bc1bitmask |= uint32_t(BCCode::B) << (p * 2);
|
||||
bc4bitmask |= uint64_t(BCCode::B) << (p * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_ERROR("Found pixel that isn't A, or B!");
|
||||
}
|
||||
}
|
||||
|
||||
byte a8[4], b8[4];
|
||||
uint16_t aHalf[4], bHalf[4];
|
||||
int16_t *aHalfS = (int16_t *)aHalf;
|
||||
int16_t *bHalfS = (int16_t *)bHalf;
|
||||
|
||||
uint16_t a565 = 0;
|
||||
uint16_t b565 = 0;
|
||||
|
||||
if(cfg.data == DataType::SNorm)
|
||||
{
|
||||
int8_t *ia8 = (int8_t *)a8;
|
||||
int8_t *ib8 = (int8_t *)b8;
|
||||
|
||||
ia8[0] = int8_t(round(a.x * -127.0f));
|
||||
ia8[1] = int8_t(round(a.y * -127.0f));
|
||||
ia8[2] = int8_t(round(a.z * -127.0f));
|
||||
ia8[3] = int8_t(round(a.w * -127.0f));
|
||||
|
||||
ib8[0] = int8_t(round(b.x * -127.0f));
|
||||
ib8[1] = int8_t(round(b.y * -127.0f));
|
||||
ib8[2] = int8_t(round(b.z * -127.0f));
|
||||
ib8[3] = int8_t(round(b.w * -127.0f));
|
||||
|
||||
aHalf[0] = MakeHalf(-a.x);
|
||||
aHalf[1] = MakeHalf(-a.y);
|
||||
aHalf[2] = MakeHalf(-a.z);
|
||||
aHalf[3] = MakeHalf(-a.w);
|
||||
|
||||
bHalf[0] = MakeHalf(-b.x);
|
||||
bHalf[1] = MakeHalf(-b.y);
|
||||
bHalf[2] = MakeHalf(-b.z);
|
||||
bHalf[3] = MakeHalf(-b.w);
|
||||
}
|
||||
else
|
||||
{
|
||||
a8[0] = byte(round(a.x * 255.0f));
|
||||
a8[1] = byte(round(a.y * 255.0f));
|
||||
a8[2] = byte(round(a.z * 255.0f));
|
||||
a8[3] = byte(round(a.w * 255.0f));
|
||||
|
||||
// red
|
||||
a565 |= byte(round(a.x * 31.0f)) << 11;
|
||||
// green
|
||||
a565 |= byte(round(a.y * 63.0f)) << 5;
|
||||
// blue
|
||||
a565 |= byte(round(a.z * 31.0f)) << 0;
|
||||
|
||||
b8[0] = byte(round(b.x * 255.0f));
|
||||
b8[1] = byte(round(b.y * 255.0f));
|
||||
b8[2] = byte(round(b.z * 255.0f));
|
||||
b8[3] = byte(round(b.w * 255.0f));
|
||||
|
||||
// red
|
||||
b565 |= byte(round(b.x * 31.0f)) << 11;
|
||||
// green
|
||||
b565 |= byte(round(b.y * 63.0f)) << 5;
|
||||
// blue
|
||||
b565 |= byte(round(b.z * 31.0f)) << 0;
|
||||
|
||||
aHalf[0] = MakeHalf(a.x);
|
||||
aHalf[1] = MakeHalf(a.y);
|
||||
aHalf[2] = MakeHalf(a.z);
|
||||
aHalf[3] = MakeHalf(a.w);
|
||||
|
||||
bHalf[0] = MakeHalf(b.x);
|
||||
bHalf[1] = MakeHalf(b.y);
|
||||
bHalf[2] = MakeHalf(b.z);
|
||||
bHalf[3] = MakeHalf(b.w);
|
||||
}
|
||||
|
||||
struct BC1
|
||||
{
|
||||
uint16_t a565;
|
||||
uint16_t b565;
|
||||
uint32_t bitmask;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BC1) == 8, "BC1 struct is mis-sized");
|
||||
|
||||
struct BC4
|
||||
{
|
||||
uint64_t a : 8;
|
||||
uint64_t b : 8;
|
||||
uint64_t bitmask : 48;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BC4) == 8, "BC4 struct is mis-sized");
|
||||
|
||||
if(bc2alpha)
|
||||
{
|
||||
uint64_t alphaBits = 0;
|
||||
|
||||
for(uint32_t p = 0; p < blockWidth * blockHeight; p++)
|
||||
{
|
||||
BCCode code = BCCode((bc1bitmask & (0x3 << (p * 2))) >> (p * 2));
|
||||
if(code == BCCode::A)
|
||||
alphaBits |= uint64_t(a8[3] >> 4) << (p * 4);
|
||||
else if(code == BCCode::B)
|
||||
alphaBits |= uint64_t(b8[3] >> 4) << (p * 4);
|
||||
}
|
||||
|
||||
memcpy(out, &alphaBits, sizeof(alphaBits));
|
||||
out += sizeof(alphaBits);
|
||||
}
|
||||
else if(bc3alpha)
|
||||
{
|
||||
// basically the same layout just a different meaning for codes above 1, which
|
||||
// we
|
||||
// don't use
|
||||
BC4 *alpha = (BC4 *)out;
|
||||
alpha->a = a8[3];
|
||||
alpha->b = b8[3];
|
||||
alpha->bitmask = bc4bitmask;
|
||||
out += sizeof(BC4);
|
||||
}
|
||||
|
||||
if(bc1)
|
||||
{
|
||||
BC1 *rgb = (BC1 *)out;
|
||||
// we don't care about color0 <= color1 order
|
||||
rgb->a565 = a565;
|
||||
rgb->b565 = b565;
|
||||
rgb->bitmask = bc1bitmask;
|
||||
out += sizeof(BC1);
|
||||
}
|
||||
|
||||
for(int ch = 0; ch < bc4channels; ch++)
|
||||
{
|
||||
BC4 *alpha = (BC4 *)out;
|
||||
alpha->a = a8[ch];
|
||||
alpha->b = b8[ch];
|
||||
alpha->bitmask = bc4bitmask;
|
||||
out += sizeof(BC4);
|
||||
}
|
||||
|
||||
uint64_t bc67indexbits = 0;
|
||||
|
||||
if(bc6 || bc7)
|
||||
{
|
||||
for(uint32_t p = 0; p < blockWidth * blockHeight; p++)
|
||||
{
|
||||
BCCode code = BCCode((bc1bitmask & (0x3 << (p * 2))) >> (p * 2));
|
||||
|
||||
if(p == 0)
|
||||
{
|
||||
// the first colour we came across should have been assigned code A. We
|
||||
// require this, because we're missing a bit from the first index
|
||||
TEST_ASSERT(code == BCCode::A, "First code must be code A when encoding BC6");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(code == BCCode::A)
|
||||
{
|
||||
bc67indexbits |= uint64_t(0) << ((p * 4) - 1);
|
||||
}
|
||||
else if(code == BCCode::B)
|
||||
{
|
||||
bc67indexbits |= uint64_t(15) << ((p * 4) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(bc6)
|
||||
{
|
||||
byte mode = 0x03;
|
||||
// mode 3: no transformed endpoints, 0 partition bits, 10 endpoint bits per
|
||||
// channel, no delta bits.
|
||||
|
||||
uint16_t bias = 0;
|
||||
|
||||
if(cfg.data == DataType::SNorm)
|
||||
{
|
||||
// final quantize step, the absolute value gets scaled a little
|
||||
for(int ch = 0; ch < 3; ch++)
|
||||
{
|
||||
bool negA = (aHalf[ch] & 0x8000) != 0;
|
||||
bool negB = (bHalf[ch] & 0x8000) != 0;
|
||||
|
||||
int16_t valA = int16_t(((aHalf[ch] & 0x7fff) * 32) / 31);
|
||||
int16_t valB = int16_t(((bHalf[ch] & 0x7fff) * 32) / 31);
|
||||
|
||||
aHalfS[ch] = (negA ? -valA : valA);
|
||||
bHalfS[ch] = (negB ? -valB : valB);
|
||||
}
|
||||
|
||||
bias = 63;
|
||||
}
|
||||
else
|
||||
{
|
||||
// final quantize step, such that max representable half float is 65504.0
|
||||
// (which gets mapped to 0xffff)
|
||||
for(int ch = 0; ch < 3; ch++)
|
||||
{
|
||||
aHalf[ch] = uint32_t(aHalf[ch] * 64) / 31;
|
||||
bHalf[ch] = uint32_t(bHalf[ch] * 64) / 31;
|
||||
}
|
||||
|
||||
bias = 15;
|
||||
}
|
||||
|
||||
uint64_t colorbits = 0;
|
||||
|
||||
byte colorbit65 = 0;
|
||||
|
||||
// 10 bits for each value, RGB for A then RGB for B
|
||||
colorbits |= uint64_t((aHalf[0] + bias) >> 6) << 0;
|
||||
colorbits |= uint64_t((aHalf[1] + bias) >> 6) << 10;
|
||||
colorbits |= uint64_t((aHalf[2] + bias) >> 6) << 20;
|
||||
|
||||
colorbits |= uint64_t((bHalf[0] + bias) >> 6) << 30;
|
||||
colorbits |= uint64_t((bHalf[1] + bias) >> 6) << 40;
|
||||
colorbits |= uint64_t((bHalf[2] + bias) >> 6) << 50; // overflows by 1 bit
|
||||
|
||||
colorbit65 = (bHalf[2] >> 15) & 0x1;
|
||||
|
||||
uint64_t block[2];
|
||||
|
||||
// first 64 bits are mode, and 59 of the color bits.
|
||||
block[0] = mode << 0;
|
||||
block[0] |= colorbits << 5;
|
||||
|
||||
// second 64-bit is the top bit of the colors bits, then the index bits
|
||||
block[1] = (bc67indexbits << 1) | colorbit65;
|
||||
|
||||
memcpy(out, block, sizeof(block));
|
||||
out += sizeof(block);
|
||||
}
|
||||
|
||||
#define ROUND_7BIT(x) ((x) >> 1)
|
||||
#define LO_BIT(x) ((x)&0x1)
|
||||
|
||||
if(bc7)
|
||||
{
|
||||
byte mode = 0x40;
|
||||
// x1000000 = mode 6: no partition bits, no rotation bits, no index selection
|
||||
// bit.
|
||||
// 7 color bits, 7 alpha bits, 1 endpoint p-bit, 0 shared p-bits, 4 index bits,
|
||||
// 0 secondary index bits
|
||||
|
||||
// color is stored R0, R1, G0, G1, B0, B1 because we only have one subset
|
||||
uint64_t colorbits = 0;
|
||||
colorbits |= uint64_t(ROUND_7BIT(a8[0])) << 0;
|
||||
colorbits |= uint64_t(ROUND_7BIT(b8[0])) << 7;
|
||||
colorbits |= uint64_t(ROUND_7BIT(a8[1])) << 14;
|
||||
colorbits |= uint64_t(ROUND_7BIT(b8[1])) << 21;
|
||||
colorbits |= uint64_t(ROUND_7BIT(a8[2])) << 28;
|
||||
colorbits |= uint64_t(ROUND_7BIT(b8[2])) << 35;
|
||||
|
||||
uint64_t alphabits = 0;
|
||||
alphabits |= uint64_t(ROUND_7BIT(a8[3])) << 0;
|
||||
alphabits |= uint64_t(ROUND_7BIT(b8[3])) << 7;
|
||||
|
||||
byte endpointA = 0;
|
||||
byte endpointB = 0;
|
||||
// take a vote, if more than two of the original values have the low bit set,
|
||||
// set
|
||||
// the endpoint. The tie-break is towards zero because we're wanting *more* than
|
||||
// two (so exactly two means 0)
|
||||
|
||||
if(LO_BIT(a8[0]) + LO_BIT(a8[1]) + LO_BIT(a8[2]) + LO_BIT(a8[3]) > 2)
|
||||
endpointA = 1;
|
||||
|
||||
if(LO_BIT(b8[0]) + LO_BIT(b8[1]) + LO_BIT(b8[2]) + LO_BIT(b8[3]) > 2)
|
||||
endpointB = 1;
|
||||
|
||||
uint64_t block[2];
|
||||
|
||||
// first 64 bits are mode, color, alpha, and endpoint A
|
||||
block[0] = mode << 0;
|
||||
block[0] |= colorbits << 7;
|
||||
block[0] |= alphabits << (7 + 42);
|
||||
block[0] |= uint64_t(endpointA & 0x1) << (7 + 42 + 14);
|
||||
|
||||
// second 64-bit is endpoint B, then the index bits
|
||||
block[1] = (bc67indexbits << 1) | endpointB;
|
||||
|
||||
memcpy(out, block, sizeof(block));
|
||||
out += sizeof(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcPixels += floatdata.slicePitch / sizeof(Vec4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace TextureZoo
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,3 +3,4 @@ from .capture import *
|
||||
from .runner import *
|
||||
from .analyse import *
|
||||
from .testcase import *
|
||||
from .shared.Texture_Zoo import *
|
||||
|
||||
@@ -341,6 +341,23 @@ def vulkan_register():
|
||||
rd.UpdateVulkanLayerRegistration(True)
|
||||
|
||||
|
||||
def launch_remote_server():
|
||||
# Fork the interpreter to run the test, in case it crashes we can catch it.
|
||||
# We can re-run with the same parameters
|
||||
args = sys.argv.copy()
|
||||
args.insert(0, sys.executable)
|
||||
|
||||
# Add parameter to run the remote server itself
|
||||
args.append('--internal_remote_server')
|
||||
|
||||
subprocess.Popen(args)
|
||||
return
|
||||
|
||||
|
||||
def become_remote_server():
|
||||
rd.BecomeRemoteServer('localhost', None, None)
|
||||
|
||||
|
||||
def internal_run_test(test_name):
|
||||
testcases = get_tests()
|
||||
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
import renderdoc as rd
|
||||
import rdtest
|
||||
from typing import List, Tuple
|
||||
import time
|
||||
import os
|
||||
|
||||
|
||||
# Not a real test, re-used by API-specific tests
|
||||
class Texture_Zoo():
|
||||
def __init__(self):
|
||||
self.proxied = False
|
||||
self.fake_msaa = False
|
||||
self.textures = {}
|
||||
self.filename = ''
|
||||
self.textures = {}
|
||||
self.controller: rd.ReplayController
|
||||
self.controller = None
|
||||
self.opengl_mode = False
|
||||
|
||||
def sub(self, mip: int, slice: int, sample: int):
|
||||
if self.fake_msaa:
|
||||
return rd.Subresource(mip, slice * 2 + sample, 0)
|
||||
else:
|
||||
return rd.Subresource(mip, slice, sample)
|
||||
|
||||
def pick(self, tex: rd.ResourceId, x: int, y: int, sub: rd.Subresource, typeCast: rd.CompType):
|
||||
if self.opengl_mode:
|
||||
y = max(1, self.textures[tex].height >> sub.mip) - 1 - y
|
||||
|
||||
return self.controller.PickPixel(tex, x, y, sub, typeCast)
|
||||
|
||||
TEST_CAPTURE = 0
|
||||
TEST_DDS = 1
|
||||
TEST_PNG = 2
|
||||
|
||||
def check_test(self, fmt_name: str, name: str, test_mode: int):
|
||||
pipe: rd.PipeState = self.controller.GetPipelineState()
|
||||
|
||||
image_view = (test_mode != Texture_Zoo.TEST_CAPTURE)
|
||||
|
||||
if image_view:
|
||||
bound_res: rd.BoundResource = pipe.GetOutputTargets()[0]
|
||||
else:
|
||||
bound_res: rd.BoundResource = pipe.GetReadOnlyResources(rd.ShaderStage.Pixel)[0].resources[0]
|
||||
|
||||
texs = self.controller.GetTextures()
|
||||
for t in texs:
|
||||
self.textures[t.resourceId] = t
|
||||
|
||||
tex_id: rd.ResourceId = bound_res.resourceId
|
||||
tex: rd.TextureDescription = self.textures[tex_id]
|
||||
|
||||
comp_type: rd.CompType = tex.format.compType
|
||||
if bound_res.typeCast != rd.CompType.Typeless:
|
||||
comp_type = bound_res.typeCast
|
||||
|
||||
# When not running proxied, save non-typecasted textures to disk
|
||||
if not image_view and not self.proxied and (tex.format.compType == comp_type or
|
||||
tex.format.type == rd.ResourceFormatType.D24S8 or
|
||||
tex.format.type == rd.ResourceFormatType.D32S8):
|
||||
save_data = rd.TextureSave()
|
||||
save_data.resourceId = tex_id
|
||||
save_data.destType = rd.FileType.DDS
|
||||
save_data.sample.mapToArray = True
|
||||
|
||||
self.textures[self.filename] = tex
|
||||
|
||||
path = rdtest.get_tmp_path(self.filename + '.dds')
|
||||
|
||||
success: bool = self.controller.SaveTexture(save_data, path)
|
||||
|
||||
if not success:
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
save_data.destType = rd.FileType.PNG
|
||||
save_data.slice.sliceIndex = 0
|
||||
save_data.sample.sampleIndex = 0
|
||||
path = path.replace('.dds', '.png')
|
||||
|
||||
if comp_type == rd.CompType.UInt:
|
||||
save_data.comp.blackPoint = 0.0
|
||||
save_data.comp.whitePoint = 255.0
|
||||
elif comp_type == rd.CompType.SInt:
|
||||
save_data.comp.blackPoint = -255.0
|
||||
save_data.comp.whitePoint = 0.0
|
||||
elif comp_type == rd.CompType.SNorm:
|
||||
save_data.comp.blackPoint = -1.0
|
||||
save_data.comp.whitePoint = 0.0
|
||||
|
||||
success: bool = self.controller.SaveTexture(save_data, path)
|
||||
|
||||
if not success:
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
value0 = []
|
||||
|
||||
comp_count = tex.format.compCount
|
||||
|
||||
# When viewing PNGs only compare the components that the original texture had
|
||||
if test_mode == Texture_Zoo.TEST_PNG:
|
||||
comp_count = self.textures[self.filename]
|
||||
tex.msSamp = 0
|
||||
tex.arraysize = 1
|
||||
tex.depth = 1
|
||||
self.fake_msaa = 'MSAA' in name
|
||||
elif test_mode == Texture_Zoo.TEST_DDS:
|
||||
tex.arraysize = self.textures[self.filename].arraysize
|
||||
tex.msSamp = self.textures[self.filename].msSamp
|
||||
self.fake_msaa = 'MSAA' in name
|
||||
|
||||
# HACK: We don't properly support BGRX, so just drop the alpha channel. We can't set this to compCount = 3
|
||||
# internally because that's a 24-bit format with no padding...
|
||||
if 'B8G8R8X8' in fmt_name:
|
||||
comp_count = 3
|
||||
|
||||
# Completely ignore the alpha for BC1, our encoder doesn't pay attention to it
|
||||
if tex.format.type == rd.ResourceFormatType.BC1:
|
||||
comp_count = 3
|
||||
|
||||
# Calculate format-appropriate epsilon
|
||||
eps_significand = 1.0
|
||||
# Account for the sRGB curve by more generous epsilon
|
||||
|
||||
if comp_type == rd.CompType.UNormSRGB:
|
||||
eps_significand = 2.5
|
||||
# Similarly SNorm essentially loses a bit of accuracy due to us only using negative values
|
||||
elif comp_type == rd.CompType.SNorm:
|
||||
eps_significand = 2.0
|
||||
|
||||
if tex.format.type == rd.ResourceFormatType.R4G4B4A4 or tex.format.type == rd.ResourceFormatType.R4G4:
|
||||
eps = (eps_significand / 15.0)
|
||||
elif rd.ResourceFormatType.BC1 <= tex.format.type <= rd.ResourceFormatType.BC3:
|
||||
eps = (eps_significand / 15.0) # 4-bit precision in some channels
|
||||
elif tex.format.type == rd.ResourceFormatType.R5G5B5A1 or tex.format.type == rd.ResourceFormatType.R5G6B5:
|
||||
eps = (eps_significand / 31.0)
|
||||
elif tex.format.type == rd.ResourceFormatType.R11G11B10:
|
||||
eps = (eps_significand / 31.0) # 5-bit mantissa in blue
|
||||
elif tex.format.type == rd.ResourceFormatType.R9G9B9E5:
|
||||
eps = (eps_significand / 63.0) # we have 9 bits of data, but might lose 2-3 due to shared exponent
|
||||
elif tex.format.type == rd.ResourceFormatType.BC6 and tex.format.compType == rd.CompType.SNorm:
|
||||
eps = (eps_significand / 63.0) # Lose a bit worth of precision for the signed version
|
||||
elif rd.ResourceFormatType.BC4 <= tex.format.type <= rd.ResourceFormatType.BC7:
|
||||
eps = (eps_significand / 127.0)
|
||||
elif tex.format.compByteWidth == 1:
|
||||
eps = (eps_significand / 255.0)
|
||||
elif comp_type == rd.CompType.Depth and tex.format.compCount == 2:
|
||||
eps = (eps_significand / 255.0) # stencil is only 8-bit
|
||||
elif tex.format.type == rd.ResourceFormatType.R10G10B10A2:
|
||||
eps = (eps_significand / 1023.0)
|
||||
else:
|
||||
# half-floats have 11-bit mantissa. This epsilon is tight enough that we can be sure
|
||||
# any remaining errors are implementation inaccuracy and not our bug
|
||||
eps = (eps_significand / 2047.0)
|
||||
|
||||
for mp in range(tex.mips):
|
||||
for sl in range(max(tex.arraysize, max(1, tex.depth >> mp))):
|
||||
z = 0
|
||||
if tex.depth > 1:
|
||||
z = sl
|
||||
|
||||
for sm in range(tex.msSamp):
|
||||
for x in range(max(1, tex.width >> mp)):
|
||||
for y in range(max(1, tex.height >> mp)):
|
||||
picked: rd.PixelValue = self.pick(tex_id, x, y, self.sub(mp, sl, sm), comp_type)
|
||||
|
||||
# each 3D slice cycles the x. This only affects the primary diagonal
|
||||
offs_x = (x + z) % max(1, tex.width >> mp)
|
||||
|
||||
# The diagonal inverts the colors
|
||||
inverted = (offs_x != y)
|
||||
|
||||
# second slice adds a coarse checkerboard pattern of inversion
|
||||
if tex.arraysize > 1 and sl == 1 and ((int(x / 2) % 2) != (int(y / 2) % 2)):
|
||||
inverted = not inverted
|
||||
|
||||
if comp_type == rd.CompType.UInt or comp_type == rd.CompType.SInt:
|
||||
expected = [10, 40, 70, 100]
|
||||
|
||||
if inverted:
|
||||
expected = list(reversed(expected))
|
||||
|
||||
expected = [c + 10 * (sm + mp) for c in expected]
|
||||
|
||||
if comp_type == rd.CompType.SInt:
|
||||
picked = picked.intValue
|
||||
else:
|
||||
picked = picked.uintValue
|
||||
elif (tex.format.type == rd.ResourceFormatType.D16S8 or
|
||||
tex.format.type == rd.ResourceFormatType.D24S8 or
|
||||
tex.format.type == rd.ResourceFormatType.D32S8):
|
||||
# depth/stencil is a bit special
|
||||
expected = [0.1, 10, 100, 0.85]
|
||||
|
||||
if inverted:
|
||||
expected = list(reversed(expected))
|
||||
|
||||
expected[0] += 0.075 * (sm + mp)
|
||||
expected[1] += 10 * (sm + mp)
|
||||
|
||||
# Normalise stencil value
|
||||
expected[1] = expected[1] / 255.0
|
||||
|
||||
picked = picked.floatValue
|
||||
else:
|
||||
expected = [0.1, 0.35, 0.6, 0.85]
|
||||
|
||||
if inverted:
|
||||
expected = list(reversed(expected))
|
||||
|
||||
expected = [c + 0.075 * (sm + mp) for c in expected]
|
||||
|
||||
picked = picked.floatValue
|
||||
|
||||
# SNorm/SInt is negative
|
||||
if comp_type == rd.CompType.SNorm or comp_type == rd.CompType.SInt:
|
||||
expected = [-c for c in expected]
|
||||
|
||||
# BGRA textures have a swizzle applied
|
||||
if tex.format.BGRAOrder():
|
||||
expected[0:3] = reversed(expected[0:3])
|
||||
|
||||
# alpha channel in 10:10:10:2 has extremely low precision, and the ULP requirements mean
|
||||
# we basically can't trust anything between 0 and 1 on float formats. Just round in that
|
||||
# case as it still lets us differentiate between alpha 0.0-0.5 and 0.5-1.0
|
||||
if tex.format.type == rd.ResourceFormatType.R10G10B10A2:
|
||||
if comp_type == rd.CompType.UInt:
|
||||
expected[3] = min(3, expected[3])
|
||||
else:
|
||||
expected[3] = round(expected[3]) * 1.0
|
||||
picked[3] = round(picked[3]) * 1.0
|
||||
|
||||
# Handle 1-bit alpha
|
||||
if tex.format.type == rd.ResourceFormatType.R5G5B5A1:
|
||||
expected[3] = 1.0 if expected[3] >= 0.5 else 0.0
|
||||
picked[3] = 1.0 if picked[3] >= 0.5 else 0.0
|
||||
|
||||
# A8 picked values come out in alpha, but we want to compare against the single channel
|
||||
if tex.format.type == rd.ResourceFormatType.A8:
|
||||
picked[0] = picked[3]
|
||||
|
||||
# Clamp to number of components in the texture
|
||||
expected = expected[0:comp_count]
|
||||
picked = picked[0:comp_count]
|
||||
|
||||
if mp == 0 and sl == 0 and sm == 0 and x == 0 and y == 0:
|
||||
value0 = picked
|
||||
|
||||
# For SRGB textures picked values will come out as linear
|
||||
def srgb2linear(f):
|
||||
if f <= 0.04045:
|
||||
return f / 12.92
|
||||
else:
|
||||
return ((f + 0.055) / 1.055) ** 2.4
|
||||
|
||||
if comp_type == rd.CompType.UNormSRGB:
|
||||
expected[0:3] = [srgb2linear(x) for x in expected[0:3]]
|
||||
|
||||
if test_mode == Texture_Zoo.TEST_PNG:
|
||||
orig_comp = self.textures[self.filename].format.compType
|
||||
if orig_comp == rd.CompType.SNorm or orig_comp == rd.CompType.SInt:
|
||||
expected = [1.0 - x for x in expected]
|
||||
|
||||
if not rdtest.value_compare(picked, expected, eps):
|
||||
raise rdtest.TestFailureException(
|
||||
"At ({},{}) of slice {}, mip {}, sample {} of {} {} got {}. Expected {}".format(
|
||||
x, y, sl, mp, sm, name, fmt_name, picked, expected))
|
||||
|
||||
if not image_view:
|
||||
output_tex = pipe.GetOutputTargets()[0].resourceId
|
||||
|
||||
# in the test captures pick the output texture, it should be identical to the
|
||||
# (0,0) pixel in slice 0, mip 0, sample 0
|
||||
view: rd.Viewport = pipe.GetViewport(0)
|
||||
|
||||
val: rd.PixelValue = self.pick(pipe.GetOutputTargets()[0].resourceId, int(view.x + view.width / 2),
|
||||
int(view.y + view.height / 2), rd.Subresource(), rd.CompType.Typeless)
|
||||
|
||||
picked = val.floatValue
|
||||
|
||||
# A8 picked values come out in alpha, but we want to compare against the single channel
|
||||
if tex.format.type == rd.ResourceFormatType.A8:
|
||||
picked[0] = picked[3]
|
||||
|
||||
# Clamp to number of components in the texture
|
||||
picked = picked[0:comp_count]
|
||||
|
||||
# Up-convert any non-float expected values to floats
|
||||
value0 = [float(x) for x in value0]
|
||||
|
||||
# For depth/stencil images, one of either depth or stencil should match
|
||||
if comp_type == rd.CompType.Depth and len(value0) == 2:
|
||||
if picked[0] == 0.0:
|
||||
value0[0] = 0.0
|
||||
# normalise stencil value if it isn't already
|
||||
if picked[1] > 1.0:
|
||||
picked[1] /= 255.0
|
||||
elif picked[0] > 1.0:
|
||||
# un-normalised stencil being rendered in red, match against our stencil expectation
|
||||
picked[0] /= 255.0
|
||||
value0[0] = value0[1]
|
||||
value0[1] = 0.0
|
||||
else:
|
||||
value0[1] = 0.0
|
||||
|
||||
if not rdtest.value_compare(picked, value0, eps):
|
||||
raise rdtest.TestFailureException(
|
||||
"In {} {} Top-left pixel as rendered is {}. Expected {}".format(name, fmt_name, picked, value0))
|
||||
|
||||
def check_capture_with_controller(self, proxy_api: str):
|
||||
any_failed = False
|
||||
|
||||
if proxy_api != '':
|
||||
rdtest.log.print('Running with {} local proxy'.format(proxy_api))
|
||||
self.proxied = True
|
||||
else:
|
||||
rdtest.log.print('Running on direct replay')
|
||||
self.proxied = False
|
||||
|
||||
for d in self.controller.GetDrawcalls():
|
||||
|
||||
# Check each region for the tests within
|
||||
if d.flags & rd.DrawFlags.PushMarker:
|
||||
name = ''
|
||||
tests_run = 0
|
||||
|
||||
failed = False
|
||||
|
||||
# Iterate over drawcalls in this region
|
||||
for sub in d.children:
|
||||
sub: rd.DrawcallDescription
|
||||
|
||||
if sub.flags & rd.DrawFlags.SetMarker:
|
||||
name = sub.name
|
||||
|
||||
# Check this draw
|
||||
if sub.flags & rd.DrawFlags.Drawcall:
|
||||
tests_run = tests_run + 1
|
||||
try:
|
||||
# Set this event as current
|
||||
self.controller.SetFrameEvent(sub.eventId, True)
|
||||
|
||||
self.filename = (d.name + '@' + name).replace('->', '_')
|
||||
|
||||
self.check_test(d.name, name, Texture_Zoo.TEST_CAPTURE)
|
||||
except rdtest.TestFailureException as ex:
|
||||
failed = any_failed = True
|
||||
rdtest.log.error(str(ex))
|
||||
|
||||
if not failed:
|
||||
rdtest.log.success("All {} texture tests for {} are OK".format(tests_run, d.name))
|
||||
|
||||
if not any_failed:
|
||||
if proxy_api != '':
|
||||
rdtest.log.success(
|
||||
'All textures are OK with {} as local proxy'.format(proxy_api))
|
||||
else:
|
||||
rdtest.log.success("All textures are OK on direct replay")
|
||||
else:
|
||||
raise rdtest.TestFailureException("Some tests were not as expected")
|
||||
|
||||
def check_capture(self, capture_filename: str, controller: rd.ReplayController):
|
||||
self.controller = controller
|
||||
|
||||
self.opengl_mode = (self.controller.GetAPIProperties().pipelineType == rd.GraphicsAPI.OpenGL)
|
||||
|
||||
failed = False
|
||||
|
||||
try:
|
||||
# First check with the local controller
|
||||
self.check_capture_with_controller('')
|
||||
except rdtest.TestFailureException as ex:
|
||||
rdtest.log.error(str(ex))
|
||||
failed = True
|
||||
|
||||
# Now shut it down
|
||||
self.controller.Shutdown()
|
||||
self.controller = None
|
||||
|
||||
# Launch a remote server
|
||||
rdtest.launch_remote_server()
|
||||
|
||||
# Wait for it to start
|
||||
time.sleep(0.5)
|
||||
|
||||
ret: Tuple[rd.ReplayStatus, rd.RemoteServer] = rd.CreateRemoteServerConnection('localhost')
|
||||
status, remote = ret
|
||||
|
||||
proxies = remote.LocalProxies()
|
||||
|
||||
try:
|
||||
# Try D3D11 and GL as proxies, D3D12/Vulkan technically don't have proxying implemented even though they
|
||||
# will be listed in proxies
|
||||
for api in ['D3D11', 'OpenGL']:
|
||||
if api not in proxies:
|
||||
continue
|
||||
|
||||
try:
|
||||
ret: Tuple[rd.ReplayStatus, rd.ReplayController] = remote.OpenCapture(proxies.index(api),
|
||||
capture_filename,
|
||||
rd.ReplayOptions(), None)
|
||||
status, self.controller = ret
|
||||
|
||||
# Now check with the proxy
|
||||
self.check_capture_with_controller(api)
|
||||
except ValueError:
|
||||
continue
|
||||
except rdtest.TestFailureException as ex:
|
||||
rdtest.log.error(str(ex))
|
||||
failed = True
|
||||
finally:
|
||||
self.controller.Shutdown()
|
||||
self.controller = None
|
||||
finally:
|
||||
remote.ShutdownServerAndConnection()
|
||||
|
||||
# Now iterate over all the temp images saved out, load them as captures, and check the texture.
|
||||
dir_path = rdtest.get_tmp_path('')
|
||||
|
||||
was_opengl = self.opengl_mode
|
||||
|
||||
# We iterate in filename order, so that dds files get opened before png files.
|
||||
for file in os.scandir(dir_path):
|
||||
if '.dds' not in file.name and '.png' not in file.name:
|
||||
continue
|
||||
|
||||
cap = rd.OpenCaptureFile()
|
||||
status = cap.OpenFile(file.path, 'rdc', None)
|
||||
|
||||
if status != rd.ReplayStatus.Succeeded:
|
||||
rdtest.log.error("Couldn't open {}".format(file.name))
|
||||
failed = True
|
||||
continue
|
||||
|
||||
ret: Tuple[rd.ReplayStatus, rd.ReplayController] = cap.OpenCapture(rd.ReplayOptions(), None)
|
||||
status, self.controller = ret
|
||||
|
||||
if status != rd.ReplayStatus.Succeeded:
|
||||
rdtest.log.error("Couldn't open {}".format(file.name))
|
||||
failed = True
|
||||
continue
|
||||
|
||||
self.filename = file.name.replace('.dds', '').replace('.png', '')
|
||||
|
||||
[a, b] = file.name.replace('.dds', ' (DDS)').replace('.png', ' (PNG)').split('@')
|
||||
|
||||
self.controller.SetFrameEvent(self.controller.GetDrawcalls()[0].eventId, True)
|
||||
|
||||
try:
|
||||
self.opengl_mode = False
|
||||
fmt: rd.ResourceFormat = self.controller.GetTextures()[0].format
|
||||
|
||||
is_compressed = (rd.ResourceFormatType.BC1 <= fmt.type <= rd.ResourceFormatType.BC7 or
|
||||
fmt.type == rd.ResourceFormatType.EAC or fmt.type == rd.ResourceFormatType.ETC2 or
|
||||
fmt.type == rd.ResourceFormatType.ASTC or fmt.type == rd.ResourceFormatType.PVRTC)
|
||||
|
||||
# OpenGL saves all non-compressed images to disk with a flip, since that's the expected order for
|
||||
# most formats. The effect of this is that we should apply the opengl_mode workaround for all files
|
||||
# *except* compressed textures
|
||||
if was_opengl and not is_compressed:
|
||||
self.opengl_mode = True
|
||||
|
||||
self.check_test(a, b, Texture_Zoo.TEST_DDS if '.dds' in file.name else Texture_Zoo.TEST_PNG)
|
||||
|
||||
rdtest.log.success("{} loaded with the correct data".format(file.name))
|
||||
except rdtest.TestFailureException as ex:
|
||||
rdtest.log.error(str(ex))
|
||||
failed = True
|
||||
|
||||
self.controller.Shutdown()
|
||||
self.controller = None
|
||||
|
||||
if failed:
|
||||
raise rdtest.TestFailureException("Some tests were not as expected")
|
||||
@@ -328,7 +328,8 @@ class TestCase:
|
||||
|
||||
self.check_capture()
|
||||
|
||||
self.controller.Shutdown()
|
||||
if self.controller is not None:
|
||||
self.controller.Shutdown()
|
||||
|
||||
def invoketest(self, debugMode):
|
||||
self.run()
|
||||
|
||||
@@ -250,9 +250,10 @@ FLT_EPSILON = 2.0*1.19209290E-07
|
||||
|
||||
|
||||
def value_compare(ref, data, eps=FLT_EPSILON):
|
||||
if type(ref) == float:
|
||||
if type(data) != float:
|
||||
return False
|
||||
if type(ref) == float or type(data) == float:
|
||||
# if the types are different this is probably 0.0 == 0 or something. Just compare straight by casting to floats
|
||||
if type(data) != type(data):
|
||||
return float(data) == float(ref)
|
||||
|
||||
# Special handling for NaNs - NaNs are always equal to NaNs, but NaN is never equal to any other value
|
||||
if math.isnan(ref) and math.isnan(data):
|
||||
|
||||
@@ -36,6 +36,8 @@ parser.add_argument('--debugger',
|
||||
parser.add_argument('--internal_run_test', help=argparse.SUPPRESS, type=str, required=False)
|
||||
# Internal command, when we re-run as admin to register vulkan layer
|
||||
parser.add_argument('--internal_vulkan_register', help=argparse.SUPPRESS, action="store_true", required=False)
|
||||
# Internal command, when we re-run as a remote server
|
||||
parser.add_argument('--internal_remote_server', help=argparse.SUPPRESS, action="store_true", required=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.renderdoc is not None:
|
||||
@@ -110,6 +112,8 @@ if args.debugger:
|
||||
|
||||
if args.internal_vulkan_register:
|
||||
rdtest.vulkan_register()
|
||||
elif args.internal_remote_server:
|
||||
rdtest.become_remote_server()
|
||||
elif args.internal_run_test is not None:
|
||||
rdtest.internal_run_test(args.internal_run_test)
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import renderdoc as rd
|
||||
import rdtest
|
||||
from typing import List, Tuple
|
||||
import time
|
||||
import os
|
||||
|
||||
|
||||
class D3D11_Texture_Zoo(rdtest.TestCase):
|
||||
slow_test = True
|
||||
demos_test_name = 'D3D11_Texture_Zoo'
|
||||
|
||||
def __init__(self):
|
||||
rdtest.TestCase.__init__(self)
|
||||
self.zoo_helper = rdtest.Texture_Zoo()
|
||||
|
||||
def check_capture(self):
|
||||
# This takes ownership of the controller and shuts it down when it's finished
|
||||
self.zoo_helper.check_capture(self.capture_filename, self.controller)
|
||||
self.controller = None
|
||||
@@ -0,0 +1,19 @@
|
||||
import renderdoc as rd
|
||||
import rdtest
|
||||
from typing import List, Tuple
|
||||
import time
|
||||
import os
|
||||
|
||||
|
||||
class D3D12_Texture_Zoo(rdtest.TestCase):
|
||||
slow_test = True
|
||||
demos_test_name = 'D3D12_Texture_Zoo'
|
||||
|
||||
def __init__(self):
|
||||
rdtest.TestCase.__init__(self)
|
||||
self.zoo_helper = rdtest.Texture_Zoo()
|
||||
|
||||
def check_capture(self):
|
||||
# This takes ownership of the controller and shuts it down when it's finished
|
||||
self.zoo_helper.check_capture(self.capture_filename, self.controller)
|
||||
self.controller = None
|
||||
@@ -0,0 +1,19 @@
|
||||
import renderdoc as rd
|
||||
import rdtest
|
||||
from typing import List, Tuple
|
||||
import time
|
||||
import os
|
||||
|
||||
|
||||
class GL_Texture_Zoo(rdtest.TestCase):
|
||||
slow_test = True
|
||||
demos_test_name = 'GL_Texture_Zoo'
|
||||
|
||||
def __init__(self):
|
||||
rdtest.TestCase.__init__(self)
|
||||
self.zoo_helper = rdtest.Texture_Zoo()
|
||||
|
||||
def check_capture(self):
|
||||
# This takes ownership of the controller and shuts it down when it's finished
|
||||
self.zoo_helper.check_capture(self.capture_filename, self.controller)
|
||||
self.controller = None
|
||||
@@ -0,0 +1,19 @@
|
||||
import renderdoc as rd
|
||||
import rdtest
|
||||
from typing import List, Tuple
|
||||
import time
|
||||
import os
|
||||
|
||||
|
||||
class VK_Texture_Zoo(rdtest.TestCase):
|
||||
slow_test = True
|
||||
demos_test_name = 'VK_Texture_Zoo'
|
||||
|
||||
def __init__(self):
|
||||
rdtest.TestCase.__init__(self)
|
||||
self.zoo_helper = rdtest.Texture_Zoo()
|
||||
|
||||
def check_capture(self):
|
||||
# This takes ownership of the controller and shuts it down when it's finished
|
||||
self.zoo_helper.check_capture(self.capture_filename, self.controller)
|
||||
self.controller = None
|
||||
Reference in New Issue
Block a user