#include "tools/unit-test/slang-unit-test.h" #include "slang-gfx.h" #include "gfx-test-util.h" #include "tools/gfx-util/shader-cursor.h" #include "source/core/slang-basic.h" #if SLANG_WINDOWS_FAMILY #include #endif using namespace Slang; using namespace gfx; namespace gfx_test { struct ValidationTextureFormatBase : RefObject { virtual void validateBlocksEqual(const void* actual, const void* expected) = 0; virtual void initializeTexel(void* texel, int x, int y, int z, int mipLevel, int arrayLayer) = 0; }; template struct ValidationTextureFormat : ValidationTextureFormatBase { int componentCount; ValidationTextureFormat(int componentCount) : componentCount(componentCount) {}; virtual void validateBlocksEqual(const void* actual, const void* expected) override { auto a = (const T*)actual; auto e = (const T*)expected; for (Int i = 0; i < componentCount; ++i) { SLANG_CHECK(a[i] == e[i]); } } virtual void initializeTexel(void* texel, int x, int y, int z, int mipLevel, int arrayLayer) override { auto temp = (T*)texel; switch (componentCount) { case 1: temp[0] = T(x + y + z + mipLevel + arrayLayer); break; case 2: temp[0] = T(x + z + arrayLayer); temp[1] = T(y + mipLevel); break; case 3: temp[0] = T(x + mipLevel); temp[1] = T(y + arrayLayer); temp[2] = T(z); break; case 4: temp[0] = T(x + arrayLayer); temp[1] = (T)y; temp[2] = (T)z; temp[3] = (T)mipLevel; break; default: assert(!"component count should be no greater than 4"); SLANG_CHECK_ABORT(false); } } }; template struct PackedValidationTextureFormat : ValidationTextureFormatBase { int rBits; int gBits; int bBits; int aBits; PackedValidationTextureFormat(int rBits, int gBits, int bBits, int aBits) : rBits(rBits), gBits(gBits), bBits(bBits), aBits(aBits) {}; virtual void validateBlocksEqual(const void* actual, const void* expected) override { T a[4]; T e[4]; unpackTexel(*(const T*)actual, a); unpackTexel(*(const T*)expected, e); for (Int i = 0; i < 4; ++i) { SLANG_CHECK(a[i] == e[i]); } } virtual void initializeTexel(void* texel, int x, int y, int z, int mipLevel, int arrayLayer) override { T temp = 0; // The only formats which currently use this have either 3 or 4 channels. TODO: BC formats? if (aBits == 0) { temp |= z; temp <<= gBits; temp |= (y + arrayLayer); temp <<= rBits; temp |= (x + mipLevel); } else { temp |= mipLevel; temp <<= bBits; temp |= z; temp <<= gBits; temp |= y; temp <<= rBits; temp |= (x + arrayLayer); } *(T*)texel = temp; } void unpackTexel(T texel, T* outComponents) { outComponents[0] = texel & ((1 << rBits) - 1); texel >>= rBits; outComponents[1] = texel & ((1 << gBits) - 1); texel >>= gBits; outComponents[2] = texel & ((1 << bBits) - 1); texel >>= bBits; outComponents[3] = texel & ((1 << aBits) - 1); texel >>= aBits; //for () } }; RefPtr getValidationTextureFormat(Format format) { switch (format) { case Format::R32G32B32A32_TYPELESS: return new ValidationTextureFormat(4); case Format::R32G32B32_TYPELESS: return new ValidationTextureFormat(3); case Format::R32G32_TYPELESS: return new ValidationTextureFormat(2); case Format::R32_TYPELESS: return new ValidationTextureFormat(1); case Format::R16G16B16A16_TYPELESS: return new ValidationTextureFormat(4); case Format::R16G16_TYPELESS: return new ValidationTextureFormat(2); case Format::R16_TYPELESS: return new ValidationTextureFormat(1); case Format::R8G8B8A8_TYPELESS: return new ValidationTextureFormat(4); case Format::R8G8_TYPELESS: return new ValidationTextureFormat(2); case Format::R8_TYPELESS: return new ValidationTextureFormat(1); case Format::B8G8R8A8_TYPELESS: return new ValidationTextureFormat(4); case Format::R32G32B32A32_FLOAT: return new ValidationTextureFormat(4); case Format::R32G32B32_FLOAT: return new ValidationTextureFormat(3); case Format::R32G32_FLOAT: return new ValidationTextureFormat(2); case Format::R32_FLOAT: return new ValidationTextureFormat(1); case Format::R16G16B16A16_FLOAT: return new ValidationTextureFormat(4); case Format::R16G16_FLOAT: return new ValidationTextureFormat(2); case Format::R16_FLOAT: return new ValidationTextureFormat(1); case Format::R32G32B32A32_UINT: return new ValidationTextureFormat(4); case Format::R32G32B32_UINT: return new ValidationTextureFormat(3); case Format::R32G32_UINT: return new ValidationTextureFormat(2); case Format::R32_UINT: return new ValidationTextureFormat(1); case Format::R16G16B16A16_UINT: return new ValidationTextureFormat(4); case Format::R16G16_UINT: return new ValidationTextureFormat(2); case Format::R16_UINT: return new ValidationTextureFormat(1); case Format::R8G8B8A8_UINT: return new ValidationTextureFormat(4); case Format::R8G8_UINT: return new ValidationTextureFormat(2); case Format::R8_UINT: return new ValidationTextureFormat(1); case Format::R32G32B32A32_SINT: return new ValidationTextureFormat(4); case Format::R32G32B32_SINT: return new ValidationTextureFormat(3); case Format::R32G32_SINT: return new ValidationTextureFormat(2); case Format::R32_SINT: return new ValidationTextureFormat(1); case Format::R16G16B16A16_SINT: return new ValidationTextureFormat(4); case Format::R16G16_SINT: return new ValidationTextureFormat(2); case Format::R16_SINT: return new ValidationTextureFormat(1); case Format::R8G8B8A8_SINT: return new ValidationTextureFormat(4); case Format::R8G8_SINT: return new ValidationTextureFormat(2); case Format::R8_SINT: return new ValidationTextureFormat(1); case Format::R16G16B16A16_UNORM: return new ValidationTextureFormat(4); case Format::R16G16_UNORM: return new ValidationTextureFormat(2); case Format::R16_UNORM: return new ValidationTextureFormat(1); case Format::R8G8B8A8_UNORM: return new ValidationTextureFormat(4); case Format::R8G8B8A8_UNORM_SRGB: return new ValidationTextureFormat(4); case Format::R8G8_UNORM: return new ValidationTextureFormat(2); case Format::R8_UNORM: return new ValidationTextureFormat(1); case Format::B8G8R8A8_UNORM: return new ValidationTextureFormat(4); case Format::B8G8R8A8_UNORM_SRGB: return new ValidationTextureFormat(4); case Format::B8G8R8X8_UNORM: return new ValidationTextureFormat(3); case Format::B8G8R8X8_UNORM_SRGB: return new ValidationTextureFormat(3); case Format::R16G16B16A16_SNORM: return new ValidationTextureFormat(4); case Format::R16G16_SNORM: return new ValidationTextureFormat(2); case Format::R16_SNORM: return new ValidationTextureFormat(1); case Format::R8G8B8A8_SNORM: return new ValidationTextureFormat(4); case Format::R8G8_SNORM: return new ValidationTextureFormat(2); case Format::R8_SNORM: return new ValidationTextureFormat(1); case Format::D32_FLOAT: return new ValidationTextureFormat(1); case Format::D16_UNORM: return new ValidationTextureFormat(1); case Format::B4G4R4A4_UNORM: return new PackedValidationTextureFormat(4, 4, 4, 4); case Format::B5G6R5_UNORM: return new PackedValidationTextureFormat(5, 6, 5, 0); case Format::B5G5R5A1_UNORM: return new PackedValidationTextureFormat(5, 5, 5, 1); case Format::R9G9B9E5_SHAREDEXP: return new ValidationTextureFormat(1); case Format::R10G10B10A2_TYPELESS: return new PackedValidationTextureFormat(10, 10, 10, 2); case Format::R10G10B10A2_UNORM: return new PackedValidationTextureFormat(10, 10, 10, 2); case Format::R10G10B10A2_UINT: return new PackedValidationTextureFormat(10, 10, 10, 2); case Format::R11G11B10_FLOAT: return new PackedValidationTextureFormat(11, 11, 10, 0); // TODO: Add testing support for BC formats // BC1_UNORM, // BC1_UNORM_SRGB, // BC2_UNORM, // BC2_UNORM_SRGB, // BC3_UNORM, // BC3_UNORM_SRGB, // BC4_UNORM, // BC4_SNORM, // BC5_UNORM, // BC5_SNORM, // BC6H_UF16, // BC6H_SF16, // BC7_UNORM, // BC7_UNORM_SRGB, default: return nullptr; } } struct TextureInfo { ITextureResource::Size extent; int numMipLevels; int arraySize; ITextureResource::SubresourceData const* initData; }; struct ValidationTextureData : RefObject { const void* textureData; ITextureResource::Size extents; ITextureResource::Offset3D strides; void* getBlockAt(Int x, Int y, Int z) { assert(x >= 0 && x < extents.width); assert(y >= 0 && y < extents.height); assert(z >= 0 && z < extents.depth); char* layerData = (char*)textureData + z * strides.z; char* rowData = layerData + y * strides.y; return rowData + x * strides.x; } }; struct TextureToTextureCopyInfo { SubresourceRange srcSubresource; SubresourceRange dstSubresource; ITextureResource::Size extent; ITextureResource::Offset3D srcOffset; ITextureResource::Offset3D dstOffset; }; struct TextureToBufferCopyInfo { SubresourceRange srcSubresource; ITextureResource::Size extent; ITextureResource::Offset3D textureOffset; size_t bufferOffset; size_t bufferSize; }; struct TextureStuff : RefObject { List> subresourceObjects; List subresourceDatas; }; struct BaseCopyTextureTest { IDevice* device; UnitTestContext* context; Format format; uint32_t texelSize; size_t alignedRowStride; ITextureResource::Type textureType; TextureInfo srcTextureInfo; TextureInfo dstTextureInfo; TextureToTextureCopyInfo texCopyInfo; TextureToBufferCopyInfo bufferCopyInfo; ComPtr srcTexture; ComPtr dstTexture; ComPtr resultsBuffer; RefPtr validationFormat; void init( IDevice* device, UnitTestContext* context, Format format, RefPtr validationFormat, ITextureResource::Type type) { this->device = device; this->context = context; this->format = format; this->validationFormat = validationFormat; this->textureType = type; FormatInfo formatInfo; GFX_CHECK_CALL_ABORT(gfxGetFormatInfo(format, &formatInfo)); this->texelSize = formatInfo.blockSizeInBytes / formatInfo.pixelsPerBlock; } RefPtr generateTextureData(int width, int height, int depth, uint32_t mipLevels, uint32_t arrayLayers) { RefPtr texture = new TextureStuff(); for (uint32_t layer = 0; layer < arrayLayers; ++layer) { for (uint32_t mip = 0; mip < mipLevels; ++mip) { RefPtr subresource = new ValidationTextureData(); auto mipWidth = Math::Max(width >> mip, 1); auto mipHeight = Math::Max(height >> mip, 1); auto mipDepth = Math::Max(depth >> mip, 1); auto mipSize = mipWidth * mipHeight * mipDepth * texelSize; subresource->textureData = malloc(mipSize); SLANG_CHECK_ABORT(subresource->textureData); subresource->extents.width = mipWidth; subresource->extents.height = mipHeight; subresource->extents.depth = mipDepth; subresource->strides.x = texelSize; subresource->strides.y = mipWidth * texelSize; subresource->strides.z = mipHeight * subresource->strides.y; texture->subresourceObjects.add(subresource); for (int z = 0; z < mipDepth; ++z) { for (int y = 0; y < mipHeight; ++y) { for (int x = 0; x < mipWidth; ++x) { auto texel = subresource->getBlockAt(x, y, z); validationFormat->initializeTexel(texel, x, y, z, mip, layer); } } } ITextureResource::SubresourceData subData = {}; subData.data = subresource->textureData; subData.strideY = subresource->strides.y; subData.strideZ = subresource->strides.z; texture->subresourceDatas.add(subData); } } return texture; } TextureAspect getTextureAspect() { switch (format) { case Format::D16_UNORM: case Format::D32_FLOAT: return TextureAspect::Depth; default: return TextureAspect::Color; } } uint32_t getSubresourceIndex(uint32_t mipLevel, uint32_t mipLevelCount, uint32_t baseArrayLayer) { return baseArrayLayer * mipLevelCount + mipLevel; } void createRequiredResources() { ITextureResource::Desc srcTexDesc = {}; srcTexDesc.type = textureType; srcTexDesc.numMipLevels = srcTextureInfo.numMipLevels; srcTexDesc.arraySize = srcTextureInfo.arraySize; srcTexDesc.size = srcTextureInfo.extent; srcTexDesc.defaultState = ResourceState::ShaderResource; srcTexDesc.allowedStates = ResourceStateSet( ResourceState::ShaderResource, ResourceState::CopySource); if (format == Format::D32_FLOAT || format == Format::D16_UNORM) { srcTexDesc.allowedStates.add(ResourceState::DepthWrite); srcTexDesc.allowedStates.add(ResourceState::DepthRead); } srcTexDesc.format = format; GFX_CHECK_CALL_ABORT(device->createTextureResource( srcTexDesc, srcTextureInfo.initData, srcTexture.writeRef())); ITextureResource::Desc dstTexDesc = {}; dstTexDesc.type = textureType; dstTexDesc.numMipLevels = dstTextureInfo.numMipLevels; dstTexDesc.arraySize = dstTextureInfo.arraySize; dstTexDesc.size = dstTextureInfo.extent; dstTexDesc.defaultState = ResourceState::CopyDestination; dstTexDesc.allowedStates = ResourceStateSet( ResourceState::ShaderResource, ResourceState::CopyDestination, ResourceState::CopySource); if (format == Format::D32_FLOAT || format == Format::D16_UNORM) { srcTexDesc.allowedStates.add(ResourceState::DepthWrite); srcTexDesc.allowedStates.add(ResourceState::DepthRead); } dstTexDesc.format = format; GFX_CHECK_CALL_ABORT(device->createTextureResource( dstTexDesc, dstTextureInfo.initData, dstTexture.writeRef())); auto bufferCopyExtents = bufferCopyInfo.extent; size_t alignment; device->getTextureRowAlignment(&alignment); alignedRowStride = (bufferCopyExtents.width * texelSize + alignment - 1) & ~(alignment - 1); IBufferResource::Desc bufferDesc = {}; bufferDesc.sizeInBytes = bufferCopyExtents.height * bufferCopyExtents.depth * alignedRowStride; bufferDesc.format = gfx::Format::Unknown; bufferDesc.elementSize = 0; bufferDesc.allowedStates = ResourceStateSet( ResourceState::ShaderResource, ResourceState::UnorderedAccess, ResourceState::CopyDestination, ResourceState::CopySource); if (format == Format::D32_FLOAT || format == Format::D16_UNORM) { srcTexDesc.allowedStates.add(ResourceState::DepthWrite); srcTexDesc.allowedStates.add(ResourceState::DepthRead); } bufferDesc.defaultState = ResourceState::CopyDestination; bufferDesc.memoryType = MemoryType::DeviceLocal; GFX_CHECK_CALL_ABORT(device->createBufferResource( bufferDesc, nullptr, resultsBuffer.writeRef())); bufferCopyInfo.bufferSize = bufferDesc.sizeInBytes; } void submitGPUWork() { Slang::ComPtr transientHeap; ITransientResourceHeap::Desc transientHeapDesc = {}; transientHeapDesc.constantBufferSize = 4096; GFX_CHECK_CALL_ABORT( device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; auto queue = device->createCommandQueue(queueDesc); auto commandBuffer = transientHeap->createCommandBuffer(); auto encoder = commandBuffer->encodeResourceCommands(); encoder->textureSubresourceBarrier(srcTexture, texCopyInfo.srcSubresource, ResourceState::ShaderResource, ResourceState::CopySource); encoder->copyTexture( dstTexture, ResourceState::CopyDestination, texCopyInfo.dstSubresource, texCopyInfo.dstOffset, srcTexture, ResourceState::CopySource, texCopyInfo.srcSubresource, texCopyInfo.srcOffset, texCopyInfo.extent); encoder->textureSubresourceBarrier(dstTexture, bufferCopyInfo.srcSubresource, ResourceState::CopyDestination, ResourceState::CopySource); encoder->copyTextureToBuffer( resultsBuffer, bufferCopyInfo.bufferOffset, bufferCopyInfo.bufferSize, alignedRowStride, dstTexture, ResourceState::CopySource, bufferCopyInfo.srcSubresource, bufferCopyInfo.textureOffset, bufferCopyInfo.extent); encoder->endEncoding(); commandBuffer->close(); queue->executeCommandBuffer(commandBuffer); queue->waitOnHost(); } bool isWithinCopyBounds(int x, int y, int z) { auto copyExtents = texCopyInfo.extent; auto copyOffset = texCopyInfo.dstOffset; auto xLowerBound = copyOffset.x; auto xUpperBound = copyOffset.x + copyExtents.width; auto yLowerBound = copyOffset.y; auto yUpperBound = copyOffset.y + copyExtents.height; auto zLowerBound = copyOffset.z; auto zUpperBound = copyOffset.z + copyExtents.depth; if (x < xLowerBound || x >= xUpperBound || y < yLowerBound || y >= yUpperBound || z < zLowerBound || z >= zUpperBound) return false; else return true; } void validateTestResults( ValidationTextureData actual, ValidationTextureData expectedCopied, ValidationTextureData expectedOriginal) { auto actualExtents = actual.extents; auto copyExtent = texCopyInfo.extent; auto srcTexOffset = texCopyInfo.srcOffset; auto dstTexOffset = texCopyInfo.dstOffset; for (Int x = 0; x < actualExtents.width; ++x) { for (Int y = 0; y < actualExtents.height; ++y) { for (Int z = 0; z < actualExtents.depth; ++z) { auto actualBlock = actual.getBlockAt(x, y, z); if (isWithinCopyBounds(x, y, z)) { // Block is located within the bounds of the source texture auto xSource = x + srcTexOffset.x - dstTexOffset.x; auto ySource = y + srcTexOffset.y - dstTexOffset.y; auto zSource = z + srcTexOffset.z - dstTexOffset.z; auto expectedBlock = expectedCopied.getBlockAt(xSource, ySource, zSource); validationFormat->validateBlocksEqual(actualBlock, expectedBlock); } else { // Block is located outside the bounds of the source texture and should be compared // against known expected values for the destination texture. auto expectedBlock = expectedOriginal.getBlockAt(x, y, z); validationFormat->validateBlocksEqual(actualBlock, expectedBlock); } } } } } void checkTestResults(ITextureResource::Size srcMipExtent, const void* expectedCopiedData, const void* expectedOriginalData) { ComPtr resultBlob; GFX_CHECK_CALL_ABORT(device->readBufferResource(resultsBuffer, 0, bufferCopyInfo.bufferSize, resultBlob.writeRef())); auto results = resultBlob->getBufferPointer(); ValidationTextureData actual; actual.extents = bufferCopyInfo.extent; actual.textureData = results; actual.strides.x = texelSize; actual.strides.y = (uint32_t)alignedRowStride; actual.strides.z = actual.extents.height * actual.strides.y; ValidationTextureData expectedCopied; expectedCopied.extents = srcMipExtent; expectedCopied.textureData = expectedCopiedData; expectedCopied.strides.x = texelSize; expectedCopied.strides.y = expectedCopied.extents.width * expectedCopied.strides.x; expectedCopied.strides.z = expectedCopied.extents.height * expectedCopied.strides.y; ValidationTextureData expectedOriginal; if (expectedOriginalData) { expectedOriginal.extents = bufferCopyInfo.extent; expectedOriginal.textureData = expectedOriginalData; expectedOriginal.strides.x = texelSize; expectedOriginal.strides.y = expectedOriginal.extents.width * expectedOriginal.strides.x; expectedOriginal.strides.z = expectedOriginal.extents.height * expectedOriginal.strides.y; } validateTestResults(actual, expectedCopied, expectedOriginal); } }; struct SimpleCopyTexture : BaseCopyTextureTest { void run() { ITextureResource::Size extent = {}; extent.width = 4; extent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 4; extent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto mipLevelCount = 1; auto arrayLayerCount = 1; auto srcTextureStuff = generateTextureData(extent.width, extent.height, extent.depth, mipLevelCount, arrayLayerCount); srcTextureInfo = { extent, mipLevelCount, arrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; dstTextureInfo = { extent, mipLevelCount, arrayLayerCount, nullptr }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent = extent; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 0, 0, 0 }; bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = extent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto subresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, mipLevelCount, srcSubresource.baseArrayLayer); auto expectedData = srcTextureStuff->subresourceDatas[subresourceIndex]; checkTestResults(extent, expectedData.data, nullptr); } }; struct CopyTextureSection : BaseCopyTextureTest { void run() { ITextureResource::Size extent = {}; extent.width = 4; extent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 4; extent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto mipLevelCount = 2; auto arrayLayerCount = (textureType == ITextureResource::Type::Texture3D) ? 1 : 2; auto srcTextureStuff = generateTextureData(extent.width, extent.height, extent.depth, mipLevelCount, arrayLayerCount); srcTextureInfo = { extent, mipLevelCount, arrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; dstTextureInfo = { extent, mipLevelCount, arrayLayerCount, nullptr }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = (textureType == ITextureResource::Type::Texture3D) ? 0 : 1; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent = extent; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 0, 0, 0 }; bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = extent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto subresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, mipLevelCount, srcSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedData = srcTextureStuff->subresourceDatas[subresourceIndex]; checkTestResults(extent, expectedData.data, nullptr); } }; struct LargeSrcToSmallDst : BaseCopyTextureTest { void run() { ITextureResource::Size srcExtent = {}; srcExtent.width = 8; srcExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 8; srcExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto srcMipLevelCount = 1; auto srcArrayLayerCount = 1; auto srcTextureStuff = generateTextureData(srcExtent.width, srcExtent.height, srcExtent.depth, srcMipLevelCount, srcArrayLayerCount); srcTextureInfo = { srcExtent, srcMipLevelCount, srcArrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; ITextureResource::Size dstExtent = {}; dstExtent.width = 4; dstExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 4; dstExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto dstMipLevelCount = 1; auto dstArrayLayerCount = 1; dstTextureInfo = { dstExtent, dstMipLevelCount, dstArrayLayerCount, nullptr }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent = dstExtent; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 0, 0, 0 }; bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = dstExtent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto subresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, srcMipLevelCount, srcSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedData = srcTextureStuff->subresourceDatas[subresourceIndex]; checkTestResults(srcExtent, expectedData.data, nullptr); } }; struct SmallSrcToLargeDst : BaseCopyTextureTest { void run() { ITextureResource::Size srcExtent = {}; srcExtent.width = 4; srcExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 4; srcExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto srcMipLevelCount = 1; auto srcArrayLayerCount = 1; auto srcTextureStuff = generateTextureData(srcExtent.width, srcExtent.height, srcExtent.depth, srcMipLevelCount, srcArrayLayerCount); srcTextureInfo = { srcExtent, srcMipLevelCount, srcArrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; ITextureResource::Size dstExtent = {}; dstExtent.width = 8; dstExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 8; dstExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto dstMipLevelCount = 1; auto dstArrayLayerCount = 1; auto dstTextureStuff = generateTextureData(dstExtent.width, dstExtent.height, dstExtent.depth, dstMipLevelCount, dstArrayLayerCount); dstTextureInfo = { dstExtent, dstMipLevelCount, dstArrayLayerCount, dstTextureStuff->subresourceDatas.getBuffer() }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent = srcExtent; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 0, 0, 0 }; bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = dstExtent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto copiedSubresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, srcMipLevelCount, srcSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedCopiedData = srcTextureStuff->subresourceDatas[copiedSubresourceIndex]; auto originalSubresourceIndex = getSubresourceIndex(dstSubresource.mipLevel, dstMipLevelCount, dstSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedOriginalData = dstTextureStuff->subresourceDatas[originalSubresourceIndex]; checkTestResults(srcExtent, expectedCopiedData.data, expectedOriginalData.data); } }; struct CopyBetweenMips : BaseCopyTextureTest { void run() { ITextureResource::Size srcExtent = {}; srcExtent.width = 16; srcExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 16; srcExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto srcMipLevelCount = 4; auto srcArrayLayerCount = 1; auto srcTextureStuff = generateTextureData(srcExtent.width, srcExtent.height, srcExtent.depth, srcMipLevelCount, srcArrayLayerCount); srcTextureInfo = { srcExtent, srcMipLevelCount, srcArrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; ITextureResource::Size dstExtent = {}; dstExtent.width = 16; dstExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 16; dstExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto dstMipLevelCount = 4; auto dstArrayLayerCount = 1; auto dstTextureStuff = generateTextureData(dstExtent.width, dstExtent.height, dstExtent.depth, dstMipLevelCount, dstArrayLayerCount); dstTextureInfo = { dstExtent, dstMipLevelCount, dstArrayLayerCount, dstTextureStuff->subresourceDatas.getBuffer() }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 2; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 1; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; auto copiedSubresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, srcMipLevelCount, srcSubresource.baseArrayLayer); auto originalSubresourceIndex = getSubresourceIndex(dstSubresource.mipLevel, dstMipLevelCount, dstSubresource.baseArrayLayer); texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent = srcTextureStuff->subresourceObjects[copiedSubresourceIndex]->extents; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 0, 0, 0 }; bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = dstTextureStuff->subresourceObjects[originalSubresourceIndex]->extents; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); ITextureResource::SubresourceData expectedCopiedData = srcTextureStuff->subresourceDatas[copiedSubresourceIndex]; ITextureResource::SubresourceData expectedOriginalData = dstTextureStuff->subresourceDatas[originalSubresourceIndex]; auto srcMipExtent = srcTextureStuff->subresourceObjects[2]->extents; checkTestResults(srcMipExtent, expectedCopiedData.data, expectedOriginalData.data); } }; struct CopyBetweenLayers : BaseCopyTextureTest { void run() { ITextureResource::Size extent = {}; extent.width = 4; extent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 4; extent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto mipLevelCount = 1; auto arrayLayerCount = (textureType == ITextureResource::Type::Texture3D) ? 1 : 2; auto srcTextureStuff = generateTextureData(extent.width, extent.height, extent.depth, mipLevelCount, arrayLayerCount); srcTextureInfo = { extent, mipLevelCount, arrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; auto dstTextureStuff = generateTextureData(extent.width, extent.height, extent.depth, mipLevelCount, arrayLayerCount); dstTextureInfo = { extent, mipLevelCount, arrayLayerCount, dstTextureStuff->subresourceDatas.getBuffer() }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = (textureType == ITextureResource::Type::Texture3D) ? 0 : 1; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent = extent; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 0, 0, 0 }; bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = extent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto copiedSubresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, mipLevelCount, srcSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedCopiedData = srcTextureStuff->subresourceDatas[copiedSubresourceIndex]; auto originalSubresourceIndex = getSubresourceIndex(dstSubresource.mipLevel, mipLevelCount, dstSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedOriginalData = dstTextureStuff->subresourceDatas[originalSubresourceIndex]; checkTestResults(extent, expectedCopiedData.data, expectedOriginalData.data); } }; struct CopyWithOffsets : BaseCopyTextureTest { void run() { ITextureResource::Size srcExtent = {}; srcExtent.width = 8; srcExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 8; srcExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto srcMipLevelCount = 1; auto srcArrayLayerCount = 1; auto srcTextureStuff = generateTextureData(srcExtent.width, srcExtent.height, srcExtent.depth, srcMipLevelCount, srcArrayLayerCount); srcTextureInfo = { srcExtent, srcMipLevelCount, srcArrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; ITextureResource::Size dstExtent = {}; dstExtent.width = 16; dstExtent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 16; dstExtent.depth = (textureType == ITextureResource::Type::Texture3D) ? 4 : 1; auto dstMipLevelCount = 1; auto dstArrayLayerCount = 1; auto dstTextureStuff = generateTextureData(dstExtent.width, dstExtent.height, dstExtent.depth, dstMipLevelCount, dstArrayLayerCount); dstTextureInfo = { dstExtent, dstMipLevelCount, dstArrayLayerCount, dstTextureStuff->subresourceDatas.getBuffer() }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent.width = 4; texCopyInfo.extent.height = 4; texCopyInfo.extent.depth = 1; texCopyInfo.srcOffset = { 2, 2, 0 }; texCopyInfo.dstOffset = { 4, 4, 0 }; if (textureType == ITextureResource::Type::Texture1D) { texCopyInfo.extent.height = 1; texCopyInfo.srcOffset.y = 0; texCopyInfo.dstOffset.y = 0; } else if (textureType == ITextureResource::Type::Texture3D) { texCopyInfo.extent.depth = srcExtent.depth; texCopyInfo.dstOffset.z = 1; } bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = dstExtent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto copiedSubresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, srcMipLevelCount, srcSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedCopiedData = srcTextureStuff->subresourceDatas[copiedSubresourceIndex]; auto originalSubresourceIndex = getSubresourceIndex(dstSubresource.mipLevel, dstMipLevelCount, dstSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedOriginalData = dstTextureStuff->subresourceDatas[originalSubresourceIndex]; checkTestResults(srcExtent, expectedCopiedData.data, expectedOriginalData.data); } }; struct CopySectionWithSetExtent : BaseCopyTextureTest { void run() { ITextureResource::Size extent = {}; extent.width = 8; extent.height = (textureType == ITextureResource::Type::Texture1D) ? 1 : 8; extent.depth = (textureType == ITextureResource::Type::Texture3D) ? 2 : 1; auto mipLevelCount = 1; auto arrayLayerCount = 1; auto srcTextureStuff = generateTextureData(extent.width, extent.height, extent.depth, mipLevelCount, arrayLayerCount); srcTextureInfo = { extent, mipLevelCount, arrayLayerCount, srcTextureStuff->subresourceDatas.getBuffer() }; auto dstTextureStuff = generateTextureData(extent.width, extent.height, extent.depth, mipLevelCount, arrayLayerCount); dstTextureInfo = { extent, mipLevelCount, arrayLayerCount, dstTextureStuff->subresourceDatas.getBuffer() }; SubresourceRange srcSubresource = {}; srcSubresource.aspectMask = getTextureAspect(); srcSubresource.mipLevel = 0; srcSubresource.mipLevelCount = 1; srcSubresource.baseArrayLayer = 0; srcSubresource.layerCount = 1; SubresourceRange dstSubresource = {}; dstSubresource.aspectMask = getTextureAspect(); dstSubresource.mipLevel = 0; dstSubresource.mipLevelCount = 1; dstSubresource.baseArrayLayer = 0; dstSubresource.layerCount = 1; texCopyInfo.srcSubresource = srcSubresource; texCopyInfo.dstSubresource = dstSubresource; texCopyInfo.extent.width = 4; texCopyInfo.extent.height = 4; texCopyInfo.extent.depth = 1; texCopyInfo.srcOffset = { 0, 0, 0 }; texCopyInfo.dstOffset = { 4, 4, 0 }; if (textureType == ITextureResource::Type::Texture1D) { texCopyInfo.extent.height = 1; texCopyInfo.dstOffset.y = 0; } else if (textureType == ITextureResource::Type::Texture3D) { texCopyInfo.extent.depth = extent.depth; } bufferCopyInfo.srcSubresource = dstSubresource; bufferCopyInfo.extent = extent; bufferCopyInfo.textureOffset = { 0, 0, 0 }; bufferCopyInfo.bufferOffset = 0; createRequiredResources(); submitGPUWork(); auto copiedSubresourceIndex = getSubresourceIndex(srcSubresource.mipLevel, mipLevelCount, srcSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedCopiedData = srcTextureStuff->subresourceDatas[copiedSubresourceIndex]; auto originalSubresourceIndex = getSubresourceIndex(dstSubresource.mipLevel, mipLevelCount, dstSubresource.baseArrayLayer); ITextureResource::SubresourceData expectedOriginalData = dstTextureStuff->subresourceDatas[originalSubresourceIndex]; checkTestResults(extent, expectedCopiedData.data, expectedOriginalData.data); } }; template void copyTextureTestImpl(IDevice* device, UnitTestContext* context) { // Skip Type::Unknown and Type::Buffer as well as Format::Unknown // TODO: Add support for TextureCube for (uint32_t i = 2; i < (uint32_t)ITextureResource::Type::CountOf - 1; ++i) { for (uint32_t j = 1; j < (uint32_t)Format::CountOf; ++j) { auto type = (ITextureResource::Type)i; auto format = (Format)j; auto validationFormat = getValidationTextureFormat(format); if (!validationFormat) continue; T test; test.init(device, context, format, validationFormat, type); test.run(); } } } SLANG_UNIT_TEST(copyTextureSimple) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copyTextureSection) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copyLargeToSmallTexture) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copySmallToLargeTexture) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copyBetweenMips) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copyBetweenLayers) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copyWithOffsets) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } SLANG_UNIT_TEST(copySectionWithSetExtent) { runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); runTestImpl(copyTextureTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } }