From 24b9d9d99a2b6cfe5871331a34dd9d686defe351 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 27 Dec 2017 01:37:15 +0100 Subject: [PATCH] [dxbc] Experimental support for ld,resinfo --- src/dxbc/dxbc_compiler.cpp | 225 ++++++++++++++++++++++++++++++++++++- src/dxbc/dxbc_compiler.h | 8 +- src/dxbc/dxbc_decoder.h | 4 + src/dxbc/dxbc_defs.cpp | 12 +- src/dxbc/dxbc_defs.h | 2 + src/spirv/spirv_module.cpp | 60 ++++++++++ src/spirv/spirv_module.h | 19 ++++ 7 files changed, 321 insertions(+), 9 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index a2ab9f84..bc48a1a2 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -33,6 +33,7 @@ namespace dxvk { // Set up common capabilities for all shaders m_module.enableCapability(spv::CapabilityShader); + m_module.enableCapability(spv::CapabilityImageQuery); // Initialize the shader module with capabilities // etc. Each shader type has its own peculiarities. @@ -65,8 +66,14 @@ namespace dxvk { case DxbcInstClass::GeometryEmit: return this->emitGeometryEmit(ins); + case DxbcInstClass::TextureQuery: + return this->emitTextureQuery(ins); + + case DxbcInstClass::TextureFetch: + return this->emitTextureFetch(ins); + case DxbcInstClass::TextureSample: - return this->emitSample(ins); + return this->emitTextureSample(ins); case DxbcInstClass::VectorAlu: return this->emitVectorAlu(ins); @@ -1309,8 +1316,212 @@ namespace dxvk { } - void DxbcCompiler::emitSample(const DxbcShaderInstruction& ins) { - // TODO support more sample ops + void DxbcCompiler::emitTextureQuery(const DxbcShaderInstruction& ins) { + // resinfo has three operands: + // (dst0) The destination register + // (src0) Resource LOD to query + // (src1) Resource to query + const DxbcResinfoType resinfoType = ins.controls.resinfoType; + + if (ins.src[1].type != DxbcOperandType::Resource) { + Logger::err("DxbcCompiler: resinfo: UAVs not yet supported"); + return; + } + + // TODO support UAVs + const uint32_t textureId = ins.src[1].idx[0].offset; + const uint32_t imageId = m_module.opLoad( + m_textures.at(textureId).colorTypeId, + m_textures.at(textureId).varId); + + // Read the exact LOD for the image query + const DxbcRegisterValue mipLod = emitRegisterLoad( + ins.src[0], DxbcRegMask(true, false, false, false)); + + // Image type, which stores the image dimensions etc. + const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo; + + const uint32_t imageDim = [&] { + switch (imageType.dim) { + case spv::Dim1D: return 1; + case spv::Dim2D: return 2; + case spv::Dim3D: return 3; + case spv::DimCube: return 2; + default: throw DxvkError("DxbcCompiler: resinfo: Unsupported image dim"); + } + }(); + + const DxbcScalarType returnType = resinfoType == DxbcResinfoType::Uint + ? DxbcScalarType::Uint32 : DxbcScalarType::Float32; + + // Query image size. This will be written to the + // first components of the destination register. + DxbcRegisterValue imageSize; + imageSize.type.ctype = DxbcScalarType::Uint32; + imageSize.type.ccount = imageDim + imageType.array; + imageSize.id = m_module.opImageQuerySizeLod( + getVectorTypeId(imageSize.type), + imageId, mipLod.id); + + // Query image levels. This will be written to + // the w component of the destination register. + DxbcRegisterValue imageLevels; + imageLevels.type.ctype = DxbcScalarType::Uint32; + imageLevels.type.ccount = 1; + imageLevels.id = m_module.opImageQueryLevels( + getVectorTypeId(imageLevels.type), + imageId); + + // Convert intermediates to the requested type + if (returnType == DxbcScalarType::Float32) { + imageSize.type.ctype = DxbcScalarType::Float32; + imageSize.id = m_module.opConvertUtoF( + getVectorTypeId(imageSize.type), + imageSize.id); + + imageLevels.type.ctype = DxbcScalarType::Float32; + imageLevels.id = m_module.opConvertUtoF( + getVectorTypeId(imageLevels.type), + imageLevels.id); + } + + // If the selected return type is rcpFloat, we need + // to compute the reciprocal of the image dimensions, + // but not the array size, so we need to separate it. + DxbcRegisterValue imageLayers; + imageLayers.type = imageSize.type; + imageLayers.id = 0; + + if (resinfoType == DxbcResinfoType::RcpFloat && imageType.array) { + imageLayers = emitRegisterExtract(imageSize, DxbcRegMask::select(imageDim)); + imageSize = emitRegisterExtract(imageSize, DxbcRegMask::firstN(imageDim)); + } + + if (resinfoType == DxbcResinfoType::RcpFloat) { + const uint32_t typeId = getVectorTypeId(imageSize.type); + + const uint32_t one = m_module.constf32(1.0f); + std::array constIds = { one, one, one, one }; + + imageSize.id = m_module.opFDiv(typeId, + m_module.constComposite(typeId, + imageSize.type.ccount, constIds.data()), + imageSize.id); + } + + // Concatenate result vectors and scalars to form a + // 4D vector. Unused components will be set to zero. + std::array vectorIds = { imageSize.id, 0, 0, 0 }; + uint32_t numVectorIds = 1; + + if (imageLayers.id != 0) + vectorIds[numVectorIds++] = imageLayers.id; + + if (imageDim + imageType.array < 3) { + const uint32_t zero = returnType == DxbcScalarType::Uint32 + ? m_module.constu32(0) + : m_module.constf32(0.0f); + + for (uint32_t i = imageDim + imageType.array; i < 3; i++) + vectorIds[numVectorIds++] = zero; + } + + vectorIds[numVectorIds++] = imageLevels.id; + + // Create the actual result vector + DxbcRegisterValue result; + result.type.ctype = returnType; + result.type.ccount = 4; + + result.id = m_module.opCompositeConstruct( + getVectorTypeId(result.type), + numVectorIds, vectorIds.data()); + + // Swizzle components using the resource swizzle + // and the destination operand's write mask + result = emitRegisterSwizzle(result, + ins.src[1].swizzle, ins.dst[0].mask); + emitRegisterStore(ins.dst[0], result); + } + + + void DxbcCompiler::emitTextureFetch(const DxbcShaderInstruction& ins) { + // ld has three operands: + // (dst0) The destination register + // (src0) Source address + // (src1) Source texture + const uint32_t textureId = ins.src[1].idx[0].offset; + + // Image type, which stores the image dimensions etc. + const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo; + + const uint32_t imageLayerDim = [&] { + switch (imageType.dim) { + case spv::DimBuffer: return 1; + case spv::Dim1D: return 1; + case spv::Dim2D: return 2; + case spv::Dim3D: return 3; + default: throw DxvkError("DxbcCompiler: ld: Unsupported image dim"); + } + }(); + + const DxbcRegMask coordArrayMask = + DxbcRegMask::firstN(imageLayerDim + imageType.array); + + // Load the texture coordinates. The last component + // contains the LOD if the resource is an image. + const DxbcRegisterValue coord = emitRegisterLoad( + ins.src[0], DxbcRegMask(true, true, true, true)); + + // Additional image operands. This will store + // the LOD and the address offset if present. + SpirvImageOperands imageOperands; + + if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) { + const std::array offsetIds = { + imageLayerDim >= 1 ? m_module.consti32(ins.sampleControls.u) : 0, + imageLayerDim >= 2 ? m_module.consti32(ins.sampleControls.v) : 0, + imageLayerDim >= 3 ? m_module.consti32(ins.sampleControls.w) : 0, + }; + + imageOperands.flags |= spv::ImageOperandsConstOffsetMask; + imageOperands.sConstOffset = m_module.constComposite( + getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }), + imageLayerDim, offsetIds.data()); + } + + if (imageType.dim != spv::DimBuffer) { + imageOperands.flags |= spv::ImageOperandsLodMask; + imageOperands.sLod = emitRegisterExtract(coord, + DxbcRegMask(false, false, false, true)).id; + } + + // Load image variable, no sampler needed + const uint32_t imageId = m_module.opLoad( + m_textures.at(textureId).colorTypeId, + m_textures.at(textureId).varId); + + // Reading a typed image or buffer view + // always returns a four-component vector. + DxbcRegisterValue result; + result.type.ctype = m_textures.at(textureId).sampledType; + result.type.ccount = 4; + + result.id = m_module.opImageFetch( + getVectorTypeId(result.type), imageId, + emitRegisterExtract(coord, coordArrayMask).id, + imageOperands); + + // Swizzle components using the texture swizzle + // and the destination operand's write mask + result = emitRegisterSwizzle(result, + ins.src[1].swizzle, ins.dst[0].mask); + emitRegisterStore(ins.dst[0], result); + } + + + void DxbcCompiler::emitTextureSample(const DxbcShaderInstruction& ins) { + // TODO support remaining sample ops // All sample instructions have at least these operands: // (dst0) The destination register @@ -1328,7 +1539,6 @@ namespace dxvk { // Image type, which stores the image dimensions etc. const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo; - // FIXME implement properly const uint32_t imageLayerDim = [&] { switch (imageType.dim) { case spv::DimBuffer: return 1; @@ -2453,13 +2663,13 @@ namespace dxvk { m_vs.builtinVertexId = emitNewBuiltinVariable({ { DxbcScalarType::Uint32, 1, 0 }, spv::StorageClassInput }, - spv::BuiltInVertexIndex, // TODO test + spv::BuiltInVertexId, // TODO test "vs_vertex_index"); m_vs.builtinInstanceId = emitNewBuiltinVariable({ { DxbcScalarType::Uint32, 1, 0 }, spv::StorageClassInput }, - spv::BuiltInInstanceIndex, // TODO test + spv::BuiltInInstanceId, // TODO test "vs_instance_index"); } @@ -2548,6 +2758,9 @@ namespace dxvk { void DxbcCompiler::emitPsInit() { + m_module.enableCapability( + spv::CapabilityDerivativeControl); + m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeOriginUpperLeft); diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index cb632230..9654ecbc 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -352,7 +352,13 @@ namespace dxvk { void emitGeometryEmit( const DxbcShaderInstruction& ins); - void emitSample( + void emitTextureQuery( + const DxbcShaderInstruction& ins); + + void emitTextureFetch( + const DxbcShaderInstruction& ins); + + void emitTextureSample( const DxbcShaderInstruction& ins); ///////////////////////////////////// diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 116b6d79..a79b4d97 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -144,6 +144,10 @@ namespace dxvk { return DxbcRegMask(n >= 1, n >= 2, n >= 3, n >= 4); } + static DxbcRegMask select(uint32_t n) { + return DxbcRegMask(n == 0, n == 1, n == 2, n == 3); + } + private: uint8_t m_mask = 0; diff --git a/src/dxbc/dxbc_defs.cpp b/src/dxbc/dxbc_defs.cpp index b74b51ae..7c4e82b0 100644 --- a/src/dxbc/dxbc_defs.cpp +++ b/src/dxbc/dxbc_defs.cpp @@ -205,7 +205,11 @@ namespace dxvk { /* Label */ { }, /* Ld */ - { }, + { 3, DxbcInstClass::TextureFetch, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, /* LdMs */ { }, /* Log */ @@ -280,7 +284,11 @@ namespace dxvk { { DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 }, } }, /* ResInfo */ - { }, + { 3, DxbcInstClass::TextureQuery, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, /* Ret */ { 0, DxbcInstClass::ControlFlow }, /* Retc */ diff --git a/src/dxbc/dxbc_defs.h b/src/dxbc/dxbc_defs.h index 2ce3c684..2080d935 100644 --- a/src/dxbc/dxbc_defs.h +++ b/src/dxbc/dxbc_defs.h @@ -32,6 +32,8 @@ namespace dxvk { CustomData, ///< Immediate constant buffer ControlFlow, ///< Control flow instructions GeometryEmit, ///< Special geometry shader instructions + TextureQuery, ///< Texture query instruction + TextureFetch, ///< Texture fetch instruction TextureSample, ///< Texture sampling instruction VectorAlu, ///< Component-wise vector instructions VectorCmov, ///< Component-wise conditional move diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index d4230b40..b215a8e7 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -1657,6 +1657,66 @@ namespace dxvk { } + uint32_t SpirvModule::opImageQuerySizeLod( + uint32_t resultType, + uint32_t image, + uint32_t lod) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpImageQuerySizeLod, 5); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(image); + m_code.putWord(lod); + return resultId; + } + + + uint32_t SpirvModule::opImageQuerySize( + uint32_t resultType, + uint32_t image) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpImageQuerySize, 4); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(image); + return resultId; + } + + + uint32_t SpirvModule::opImageQueryLevels( + uint32_t resultType, + uint32_t image) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpImageQueryLevels, 4); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(image); + return resultId; + } + + + uint32_t SpirvModule::opImageFetch( + uint32_t resultType, + uint32_t image, + uint32_t coordinates, + const SpirvImageOperands& operands) { + uint32_t resultId = this->allocateId(); + + m_code.putIns(spv::OpImageFetch, + 5 + getImageOperandWordCount(operands)); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(image); + m_code.putWord(coordinates); + + putImageOperands(operands); + return resultId; + } + + uint32_t SpirvModule::opImageSampleImplicitLod( uint32_t resultType, uint32_t sampledImage, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index ff833a95..2d7d67af 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -573,6 +573,25 @@ namespace dxvk { uint32_t image, uint32_t sampler); + uint32_t opImageQuerySizeLod( + uint32_t resultType, + uint32_t image, + uint32_t lod); + + uint32_t opImageQuerySize( + uint32_t resultType, + uint32_t image); + + uint32_t opImageQueryLevels( + uint32_t resultType, + uint32_t image); + + uint32_t opImageFetch( + uint32_t resultType, + uint32_t image, + uint32_t coordinates, + const SpirvImageOperands& operands); + uint32_t opImageSampleImplicitLod( uint32_t resultType, uint32_t sampledImage,