mirror of
https://github.com/Inori/GPCS4.git
synced 2024-06-02 11:28:07 -04:00
implement declare resource in gcn compiler
This commit is contained in:
parent
96940f8915
commit
fd8cad62de
|
@ -21,6 +21,8 @@ NamespaceIndentation: All
|
|||
UseTab: ForContinuationAndIndentation
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: true
|
||||
|
||||
ColumnLimit: 0
|
||||
SortIncludes: true
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<ClInclude Include="Graphics\Gcn\GcnAnalysis.h" />
|
||||
<ClInclude Include="Graphics\Gcn\GcnCommon.h" />
|
||||
<ClInclude Include="Graphics\Gcn\GcnCompiler.h" />
|
||||
<ClInclude Include="Graphics\Gcn\GcnCompilerDefs.h" />
|
||||
<ClInclude Include="Graphics\Gcn\GcnConstants.h" />
|
||||
<ClInclude Include="Graphics\Gcn\GcnDecoder.h" />
|
||||
<ClInclude Include="Graphics\Gcn\GcnEnum.h" />
|
||||
|
|
|
@ -898,6 +898,9 @@
|
|||
<ClInclude Include="Graphics\Gnm\GnmRenderState.h">
|
||||
<Filter>Source Files\Graphics\Gnm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Graphics\Gcn\GcnCompilerDefs.h">
|
||||
<Filter>Source Files\Graphics\Gcn</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Loader\EbootObject.cpp">
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace sce::gcn
|
|||
|
||||
struct GcnAnalysisInfo
|
||||
{
|
||||
uint32_t placeHoder;
|
||||
uint32_t placeHolder;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
#include "GcnCompiler.h"
|
||||
|
||||
#include "GcnAnalysis.h"
|
||||
#include "GcnHeader.h"
|
||||
#include "GcnUtil.h"
|
||||
#include "PlatFile.h"
|
||||
|
||||
#include "Gnm/GnmConstant.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LOG_CHANNEL(Graphic.Gcn.GcnCompiler);
|
||||
|
||||
using namespace sce::Gnm;
|
||||
using namespace sce::vlt;
|
||||
|
||||
namespace sce::gcn
|
||||
|
@ -54,7 +61,7 @@ namespace sce::gcn
|
|||
|
||||
updateProgramCounter(ins);
|
||||
}
|
||||
|
||||
|
||||
void GcnCompiler::compileInstruction(
|
||||
const GcnShaderInstruction& ins)
|
||||
{
|
||||
|
@ -138,7 +145,10 @@ namespace sce::gcn
|
|||
// Set up common capabilities for all shaders
|
||||
m_module.enableCapability(spv::CapabilityShader);
|
||||
m_module.enableCapability(spv::CapabilityImageQuery);
|
||||
|
||||
|
||||
// Declare shader resource and input interfaces
|
||||
this->emitDclInputSlots();
|
||||
|
||||
// Initialize the shader module with capabilities
|
||||
// etc. Each shader type has its own peculiarities.
|
||||
|
||||
|
@ -153,12 +163,11 @@ namespace sce::gcn
|
|||
case GcnProgramType::ComputeShader: emitCsInit(); break;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
}
|
||||
|
||||
void GcnCompiler::emitFunctionBegin(
|
||||
uint32_t entryPoint,
|
||||
uint32_t returnType,
|
||||
uint32_t entryPoint,
|
||||
uint32_t returnType,
|
||||
uint32_t funcType)
|
||||
{
|
||||
this->emitFunctionEnd();
|
||||
|
@ -214,6 +223,18 @@ namespace sce::gcn
|
|||
perVertexPointer, spv::StorageClassOutput);
|
||||
m_entryPointInterfaces.push_back(m_perVertexOut);
|
||||
m_module.setDebugName(m_perVertexOut, "vs_vertex_out");
|
||||
|
||||
|
||||
// Main function of the vertex shader
|
||||
m_vs.functionId = m_module.allocateId();
|
||||
m_module.setDebugName(m_vs.functionId, "vs_main");
|
||||
|
||||
this->emitFunctionBegin(
|
||||
m_vs.functionId,
|
||||
m_module.defVoidType(),
|
||||
m_module.defFunctionType(
|
||||
m_module.defVoidType(), 0, nullptr));
|
||||
this->emitFunctionLabel();
|
||||
}
|
||||
|
||||
void GcnCompiler::emitHsInit()
|
||||
|
@ -239,7 +260,14 @@ namespace sce::gcn
|
|||
void GcnCompiler::emitVsFinalize()
|
||||
{
|
||||
this->emitMainFunctionBegin();
|
||||
this->emitInputFetch();
|
||||
this->emitInputSetup();
|
||||
|
||||
// call fetch shader
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_vs.fetchFuncId, 0, nullptr);
|
||||
|
||||
// call vs_main
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_vs.functionId, 0, nullptr);
|
||||
|
@ -267,20 +295,614 @@ namespace sce::gcn
|
|||
{
|
||||
}
|
||||
|
||||
void GcnCompiler::emitInputFetch()
|
||||
|
||||
void GcnCompiler::emitDclInputSlots()
|
||||
{
|
||||
// Emulate fetch shader
|
||||
// Declare resource and input interfaces in input usage slot
|
||||
|
||||
// Get the flattened resource table.
|
||||
const auto& resouceTable = m_header->getShaderResourceTable();
|
||||
for (const auto& res : resouceTable)
|
||||
{
|
||||
ShaderInputUsageType usage = (ShaderInputUsageType)res.usage;
|
||||
switch (usage)
|
||||
{
|
||||
case kShaderInputUsageImmConstBuffer:
|
||||
{
|
||||
// ImmConstBuffer is different from D3D11 ImmediateConstantBuffer
|
||||
// It's not constant data embedded into the shader, it's just a simple buffer binding.
|
||||
this->emitDclBuffer(res);
|
||||
}
|
||||
break;
|
||||
case kShaderInputUsageImmResource:
|
||||
case kShaderInputUsageImmRwResource:
|
||||
{
|
||||
if (res.type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
|
||||
{
|
||||
this->emitDclBuffer(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emitDclTexture(res);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kShaderInputUsageImmSampler:
|
||||
break;
|
||||
case kShaderInputUsagePtrVertexBufferTable:
|
||||
{
|
||||
LOG_ASSERT(hasFetchShader() == true, "no fetch shader found while vertex buffer table exist.");
|
||||
// Declare vertex input
|
||||
this->emitDclVertexInput();
|
||||
// Emulate fetch shader with a function
|
||||
this->emitFetchInput();
|
||||
}
|
||||
break;
|
||||
case kShaderInputUsageImmAluFloatConst:
|
||||
case kShaderInputUsageImmAluBool32Const:
|
||||
case kShaderInputUsageImmGdsCounterRange:
|
||||
case kShaderInputUsageImmGdsMemoryRange:
|
||||
case kShaderInputUsageImmGwsBase:
|
||||
case kShaderInputUsageImmLdsEsGsSize:
|
||||
case kShaderInputUsageImmVertexBuffer:
|
||||
LOG_ASSERT(false, "TODO: usage type %d not supported.", usage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GcnCompiler::emitDclBuffer(
|
||||
const GcnShaderResource& res)
|
||||
{
|
||||
uint32_t regIdx = res.startRegister;
|
||||
|
||||
const bool asSsbo =
|
||||
(res.type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
|
||||
std::string name = util::str::formatex(asSsbo ? "sb" : "cb", regIdx);
|
||||
// Declare the uniform buffer as max supported size.
|
||||
// Will this produce bad impact?
|
||||
constexpr uint32_t MaxUniformBufferSize = 65536;
|
||||
uint32_t numConstants = asSsbo ? 0 : MaxUniformBufferSize / 16;
|
||||
|
||||
uint32_t arrayType = 0;
|
||||
if (!asSsbo)
|
||||
{
|
||||
// std140 layout uniform buffer data is stored as a fixed-size array
|
||||
// of 4x32-bit vectors. SPIR-V requires explicit strides.
|
||||
arrayType = m_module.defArrayTypeUnique(
|
||||
getVectorTypeId({ GcnScalarType::Float32, 4 }),
|
||||
m_module.constu32(numConstants));
|
||||
m_module.decorateArrayStride(arrayType, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
arrayType = m_module.defRuntimeArrayTypeUnique(
|
||||
getScalarTypeId(GcnScalarType::Float32));
|
||||
m_module.decorateArrayStride(arrayType, 4);
|
||||
}
|
||||
|
||||
// SPIR-V requires us to put that array into a
|
||||
// struct and decorate that struct as a block.
|
||||
const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);
|
||||
|
||||
m_module.decorate(structType, asSsbo
|
||||
? spv::DecorationBufferBlock
|
||||
: spv::DecorationBlock);
|
||||
m_module.memberDecorateOffset(structType, 0, 0);
|
||||
|
||||
m_module.setDebugName(structType, util::str::formatex(name.c_str(), "_t").c_str());
|
||||
m_module.setDebugMemberName(structType, 0, "m");
|
||||
|
||||
// Variable that we'll use to access the buffer
|
||||
const uint32_t varId = m_module.newVar(
|
||||
m_module.defPointerType(structType, spv::StorageClassUniform),
|
||||
spv::StorageClassUniform);
|
||||
|
||||
m_module.setDebugName(varId, name.c_str());
|
||||
|
||||
// Compute the VLT binding slot index for the buffer.
|
||||
// Gnm needs to bind the actual buffers to this slot.
|
||||
uint32_t bindingId = asSsbo ? computeResourceBinding(
|
||||
m_programInfo.type(), regIdx)
|
||||
: computeConstantBufferBinding(
|
||||
m_programInfo.type(), regIdx);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
|
||||
if (res.usage == kShaderInputUsageImmResource)
|
||||
m_module.decorate(varId, spv::DecorationNonWritable);
|
||||
|
||||
// Record the buffer so that we can use it
|
||||
// while compiling buffer instructions.
|
||||
GcnBuffer buf;
|
||||
buf.varId = varId;
|
||||
buf.size = numConstants;
|
||||
buf.asSsbo = asSsbo;
|
||||
m_buffers.at(regIdx) = buf;
|
||||
|
||||
// Store descriptor info for the shader interface
|
||||
VltResourceSlot resource;
|
||||
resource.slot = bindingId;
|
||||
resource.type = asSsbo
|
||||
? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
|
||||
: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
||||
resource.access =
|
||||
res.usage == kShaderInputUsageImmResource ?
|
||||
VK_ACCESS_SHADER_READ_BIT :
|
||||
(asSsbo ?
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT :
|
||||
VK_ACCESS_UNIFORM_READ_BIT);
|
||||
m_resourceSlots.push_back(resource);
|
||||
}
|
||||
|
||||
void GcnCompiler::emitDclTexture(
|
||||
const GcnShaderResource& res)
|
||||
{
|
||||
const uint32_t registerId = res.startRegister;
|
||||
|
||||
const auto& textureInfoTable = getTextureInfoTable();
|
||||
const auto& textureInfo = textureInfoTable[registerId];
|
||||
|
||||
// We also handle unordered access views here
|
||||
const bool isStorage = res.usage != kShaderInputUsageImmResource;
|
||||
|
||||
if (isStorage)
|
||||
{
|
||||
m_module.enableCapability(spv::CapabilityStorageImageReadWithoutFormat);
|
||||
m_module.enableCapability(spv::CapabilityStorageImageWriteWithoutFormat);
|
||||
}
|
||||
|
||||
Gnm::TextureChannelType channelType = textureInfo.channelType;
|
||||
// Declare the actual sampled type
|
||||
const GcnScalarType sampledType = [channelType]
|
||||
{
|
||||
// clang-format off
|
||||
switch (channelType)
|
||||
{
|
||||
// FIXME do we have to manually clamp writes to SNORM/UNORM resources?
|
||||
case Gnm::kTextureChannelTypeSNorm: return GcnScalarType::Float32;
|
||||
case Gnm::kTextureChannelTypeUNorm: return GcnScalarType::Float32;
|
||||
case Gnm::kTextureChannelTypeFloat: return GcnScalarType::Float32;
|
||||
case Gnm::kTextureChannelTypeSInt: return GcnScalarType::Sint32;
|
||||
case Gnm::kTextureChannelTypeUInt: return GcnScalarType::Uint32;
|
||||
default: Logger::exception(util::str::formatex("GcnCompiler: Invalid sampled type: ", channelType));
|
||||
}
|
||||
// clang-format on
|
||||
}();
|
||||
|
||||
// Declare the resource type
|
||||
Gnm::TextureType textureType = textureInfo.textureType;
|
||||
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
|
||||
const GcnImageInfo typeInfo = getImageType(
|
||||
textureType, isStorage, textureInfo.isDepth);
|
||||
|
||||
// Declare additional capabilities if necessary
|
||||
switch (textureType)
|
||||
{
|
||||
case Gnm::kTextureType1d:
|
||||
case Gnm::kTextureType1dArray:
|
||||
m_module.enableCapability(isStorage
|
||||
? spv::CapabilityImage1D
|
||||
: spv::CapabilitySampled1D);
|
||||
break;
|
||||
default:
|
||||
// No additional capabilities required
|
||||
break;
|
||||
}
|
||||
|
||||
spv::ImageFormat imageFormat = spv::ImageFormatUnknown;
|
||||
|
||||
// If the read-without-format capability is not set and this
|
||||
// image is access via a typed load, or if atomic operations
|
||||
// are used,, we must define the image format explicitly.
|
||||
//if (isStorage)
|
||||
//{
|
||||
// if ((m_analysis->uavInfos[registerId].accessAtomicOp) ||
|
||||
// (m_analysis->uavInfos[registerId].accessTypedLoad &&
|
||||
// !m_moduleInfo.options.useStorageImageReadWithoutFormat))
|
||||
// imageFormat = getScalarImageFormat(sampledType);
|
||||
//}
|
||||
|
||||
// We do not know whether the image is going to be used as
|
||||
// a color image or a depth image yet, but we can pick the
|
||||
// correct type when creating a sampled image object.
|
||||
const uint32_t imageTypeId = m_module.defImageType(sampledTypeId,
|
||||
typeInfo.dim,
|
||||
typeInfo.depth,
|
||||
typeInfo.array,
|
||||
typeInfo.ms,
|
||||
typeInfo.sampled,
|
||||
imageFormat);
|
||||
|
||||
// We'll declare the texture variable with the color type
|
||||
// and decide which one to use when the texture is sampled.
|
||||
const uint32_t resourcePtrType = m_module.defPointerType(
|
||||
imageTypeId, spv::StorageClassUniformConstant);
|
||||
|
||||
const uint32_t varId = m_module.newVar(resourcePtrType,
|
||||
spv::StorageClassUniformConstant);
|
||||
|
||||
m_module.setDebugName(varId,
|
||||
util::str::formatex(isStorage ? "r" : "t", registerId).c_str());
|
||||
|
||||
// Compute the VLT binding slot index for the resource.
|
||||
// Gnm needs to bind the actual resource to this slot.
|
||||
uint32_t bindingId = computeResourceBinding(m_programInfo.type(), registerId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
|
||||
GcnTexture tex;
|
||||
tex.imageInfo = typeInfo;
|
||||
tex.varId = varId;
|
||||
tex.sampledType = sampledType;
|
||||
tex.sampledTypeId = sampledTypeId;
|
||||
tex.imageTypeId = imageTypeId;
|
||||
tex.colorTypeId = imageTypeId;
|
||||
tex.depthTypeId = 0;
|
||||
|
||||
if (textureInfo.isDepth &&
|
||||
(sampledType == GcnScalarType::Float32) &&
|
||||
(textureType == Gnm::kTextureType2d ||
|
||||
textureType == Gnm::kTextureType2dArray ||
|
||||
textureType == Gnm::kTextureTypeCubemap))
|
||||
{
|
||||
tex.depthTypeId = m_module.defImageType(sampledTypeId,
|
||||
typeInfo.dim, 1, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
||||
spv::ImageFormatUnknown);
|
||||
}
|
||||
|
||||
m_textures.at(registerId) = tex;
|
||||
|
||||
// Store descriptor info for the shader interface
|
||||
VltResourceSlot resource;
|
||||
resource.slot = bindingId;
|
||||
resource.view = typeInfo.vtype;
|
||||
resource.access = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
if (isStorage)
|
||||
{
|
||||
resource.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
resource.access |= VK_ACCESS_SHADER_WRITE_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
}
|
||||
|
||||
m_resourceSlots.push_back(resource);
|
||||
}
|
||||
|
||||
void GcnCompiler::emitDclSampler(
|
||||
const GcnShaderResource& res)
|
||||
{
|
||||
const uint32_t samplerId = res.startRegister;
|
||||
|
||||
// The sampler type is opaque, but we still have to
|
||||
// define a pointer and a variable in oder to use it
|
||||
const uint32_t samplerType = m_module.defSamplerType();
|
||||
const uint32_t samplerPtrType = m_module.defPointerType(
|
||||
samplerType, spv::StorageClassUniformConstant);
|
||||
|
||||
// Define the sampler variable
|
||||
const uint32_t varId = m_module.newVar(samplerPtrType,
|
||||
spv::StorageClassUniformConstant);
|
||||
m_module.setDebugName(varId,
|
||||
util::str::formatex("s", samplerId).c_str());
|
||||
|
||||
m_samplers.at(samplerId).varId = varId;
|
||||
m_samplers.at(samplerId).typeId = samplerType;
|
||||
|
||||
// Compute binding slot index for the sampler
|
||||
uint32_t bindingId = computeSamplerBinding(
|
||||
m_programInfo.type(), samplerId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
|
||||
// Store descriptor info for the shader interface
|
||||
VltResourceSlot resource;
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
||||
resource.access = 0;
|
||||
m_resourceSlots.push_back(resource);
|
||||
}
|
||||
|
||||
void GcnCompiler::emitDclInput(
|
||||
const VertexInputSemantic& sema)
|
||||
{
|
||||
GcnRegisterInfo info;
|
||||
info.type.ctype = GcnScalarType::Float32;
|
||||
// the count value is fixed when parsing V# in CommandBufferDraw
|
||||
info.type.ccount = sema.m_sizeInElements;
|
||||
info.type.alength = 0;
|
||||
info.sclass = spv::StorageClassInput;
|
||||
|
||||
const uint32_t varId = emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, sema.m_semantic);
|
||||
m_module.setDebugName(varId, util::str::formatex("v", sema.m_semantic).c_str());
|
||||
|
||||
// Record the input so that we can
|
||||
// use it in fetch shader.
|
||||
GcnRegisterPointer input;
|
||||
input.type.ctype = info.type.ctype;
|
||||
input.type.ccount = info.type.ccount;
|
||||
input.id = varId;
|
||||
m_inputs[sema.m_semantic] = input;
|
||||
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
// Declare the input slot as defined
|
||||
m_interfaceSlots.inputSlots |= 1u << sema.m_semantic;
|
||||
}
|
||||
|
||||
void GcnCompiler::emitDclVertexInput()
|
||||
{
|
||||
auto table = getSemanticTable();
|
||||
|
||||
for (uint32_t i = 0; i != table.second; ++i)
|
||||
{
|
||||
auto& sema = table.first[i];
|
||||
this->emitDclInput(sema);
|
||||
}
|
||||
}
|
||||
|
||||
void GcnCompiler::emitInputSetup()
|
||||
{
|
||||
// The 16 user data registers is passed though push constants,
|
||||
// here we copy them into predefined user data array.
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
void GcnCompiler::emitFetchInput()
|
||||
{
|
||||
// Emulate fetch shader,
|
||||
// load vertex input into destination vgprs.
|
||||
m_vs.fetchFuncId = m_module.allocateId();
|
||||
m_module.setDebugName(m_vs.fetchFuncId, "vs_fetch");
|
||||
|
||||
this->emitFunctionBegin(
|
||||
m_vs.fetchFuncId,
|
||||
m_module.defVoidType(),
|
||||
m_module.defFunctionType(
|
||||
m_module.defVoidType(), 0, nullptr));
|
||||
this->emitFunctionLabel();
|
||||
|
||||
auto tablePair = getSemanticTable();
|
||||
const VertexInputSemantic* semaTable = tablePair.first;
|
||||
uint32_t semaCount = tablePair.second;
|
||||
for (uint32_t i = 0; i != semaCount; ++i)
|
||||
{
|
||||
auto& sema = semaTable[i];
|
||||
|
||||
auto value = emitValueLoad(m_inputs[sema.m_semantic]);
|
||||
GcnInstOperand reg = {};
|
||||
reg.field = GcnOperandField::VectorGPR;
|
||||
reg.code = sema.m_vgpr;
|
||||
this->emitVgprArrayStore(
|
||||
reg, sema.m_sizeInElements, value);
|
||||
}
|
||||
}
|
||||
|
||||
void GcnCompiler::emitOutputSetup()
|
||||
{
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitBuildConstVecf32(
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float w,
|
||||
uint32_t GcnCompiler::emitNewVariable(
|
||||
const GcnRegisterInfo& info)
|
||||
{
|
||||
const uint32_t ptrTypeId = this->getPointerTypeId(info);
|
||||
return m_module.newVar(ptrTypeId, info.sclass);
|
||||
}
|
||||
|
||||
uint32_t GcnCompiler::emitNewBuiltinVariable(
|
||||
const GcnRegisterInfo& info,
|
||||
spv::BuiltIn builtIn,
|
||||
const char* name)
|
||||
{
|
||||
const uint32_t varId = emitNewVariable(info);
|
||||
|
||||
m_module.setDebugName(varId, name);
|
||||
m_module.decorateBuiltIn(varId, builtIn);
|
||||
|
||||
if (m_programInfo.type() == GcnProgramType::PixelShader &&
|
||||
info.type.ctype != GcnScalarType::Float32 &&
|
||||
info.type.ctype != GcnScalarType::Bool &&
|
||||
info.sclass == spv::StorageClassInput)
|
||||
{
|
||||
m_module.decorate(varId, spv::DecorationFlat);
|
||||
}
|
||||
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
return varId;
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitVgprLoad(
|
||||
const GcnInstOperand& reg)
|
||||
{
|
||||
uint32_t vgprIndex = reg.code - GcnCodeVGPR0;
|
||||
GcnRegisterPointer& vgpr = m_vgprs[vgprIndex];
|
||||
|
||||
// vgprs are not allowed to load before created.
|
||||
// if this occurs, it is most likely a system value vgpr
|
||||
// which should be initialized in emitInputSetup,
|
||||
// please add it there.
|
||||
LOG_ASSERT(vgpr.id != 0, "vgpr v%d is not initialized before load.", vgprIndex);
|
||||
|
||||
return this->emitValueLoad(vgpr);
|
||||
}
|
||||
|
||||
void GcnCompiler::emitVgprStore(
|
||||
const GcnInstOperand& reg,
|
||||
const GcnRegisterValue& value)
|
||||
{
|
||||
uint32_t vgprIndex = reg.code - GcnCodeVGPR0;
|
||||
GcnRegisterPointer& vgpr = m_vgprs[vgprIndex];
|
||||
|
||||
// If the vgpr has not been used, we create one.
|
||||
if (vgpr.id == 0)
|
||||
{
|
||||
GcnRegisterInfo info;
|
||||
info.type.ctype = GcnScalarType::Float32;
|
||||
info.type.ccount = 1;
|
||||
info.type.alength = 0;
|
||||
info.sclass = spv::StorageClassPrivate;
|
||||
uint32_t id = emitNewVariable(info);
|
||||
|
||||
vgpr.type.ctype = info.type.ctype;
|
||||
vgpr.type.ccount = info.type.ccount;
|
||||
vgpr.id = id;
|
||||
}
|
||||
|
||||
return this->emitValueStore(vgpr, value, GcnRegMask(true, false, false, false));
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitVgprArrayLoad(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count)
|
||||
{
|
||||
|
||||
// load values in vgpr array into a vector,
|
||||
// e.g. v[4:6] -> vec3
|
||||
}
|
||||
|
||||
void GcnCompiler::emitVgprArrayStore(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count,
|
||||
const GcnRegisterValue& value)
|
||||
{
|
||||
// store values in a vector into vgpr array,
|
||||
// e.g. vec3 -> v[4:6]
|
||||
|
||||
LOG_ASSERT(count <= value.type.ccount, "value component count is less than store count.");
|
||||
|
||||
for (uint32_t i = 0; i != count ; ++i)
|
||||
{
|
||||
GcnInstOperand reg = start;
|
||||
reg.code += i;
|
||||
|
||||
uint32_t typeId = this->getScalarTypeId(value.type.ctype);
|
||||
uint32_t id = m_module.opCompositeExtract(
|
||||
typeId, value.id, 1, &i);
|
||||
|
||||
GcnRegisterValue val;
|
||||
val.type.ctype = value.type.ctype;
|
||||
val.type.ccount = 1;
|
||||
val.id = id;
|
||||
this->emitVgprStore(reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitSgprLoad(
|
||||
const GcnInstOperand& reg)
|
||||
{
|
||||
}
|
||||
|
||||
void GcnCompiler::emitSgprStore(
|
||||
const GcnInstOperand& reg,
|
||||
const GcnRegisterValue& value)
|
||||
{
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitSgprArrayLoad(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count)
|
||||
{
|
||||
// load values in vgpr array into a vector,
|
||||
// e.g. v[4:6] -> vec3
|
||||
}
|
||||
|
||||
void GcnCompiler::emitSgprArrayStore(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count,
|
||||
const GcnRegisterValue& value)
|
||||
{
|
||||
// store values in a vector into sgpr array,
|
||||
// e.g. vec3 -> s[4:6]
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitValueLoad(
|
||||
GcnRegisterPointer ptr)
|
||||
{
|
||||
GcnRegisterValue result;
|
||||
result.type = ptr.type;
|
||||
result.id = m_module.opLoad(
|
||||
getVectorTypeId(result.type),
|
||||
ptr.id);
|
||||
return result;
|
||||
}
|
||||
|
||||
void GcnCompiler::emitValueStore(
|
||||
GcnRegisterPointer ptr,
|
||||
GcnRegisterValue value,
|
||||
GcnRegMask writeMask)
|
||||
{
|
||||
// If the component types are not compatible,
|
||||
// we need to bit-cast the source variable.
|
||||
if (value.type.ctype != ptr.type.ctype)
|
||||
value = emitRegisterBitcast(value, ptr.type.ctype);
|
||||
|
||||
// If the source value consists of only one component,
|
||||
// it is stored in all components of the destination.
|
||||
if (value.type.ccount == 1)
|
||||
value = emitRegisterExtend(value, writeMask.popCount());
|
||||
|
||||
if (ptr.type.ccount == writeMask.popCount())
|
||||
{
|
||||
// Simple case: We write to the entire register
|
||||
m_module.opStore(ptr.id, value.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We only write to part of the destination
|
||||
// register, so we need to load and modify it
|
||||
GcnRegisterValue tmp = emitValueLoad(ptr);
|
||||
tmp = emitRegisterInsert(tmp, value, writeMask);
|
||||
|
||||
m_module.opStore(ptr.id, tmp.id);
|
||||
}
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitRegisterLoad(
|
||||
const GcnInstOperand& reg,
|
||||
GcnRegMask writeMask)
|
||||
{
|
||||
}
|
||||
|
||||
void GcnCompiler::emitRegisterStore(
|
||||
const GcnInstOperand& reg,
|
||||
GcnRegisterValue value)
|
||||
{
|
||||
}
|
||||
|
||||
GcnRegisterPointer GcnCompiler::emitCompositeAccess(
|
||||
GcnRegisterPointer pointer,
|
||||
spv::StorageClass sclass,
|
||||
uint32_t index)
|
||||
{
|
||||
// Create a pointer into a composite object
|
||||
|
||||
uint32_t ptrTypeId = m_module.defPointerType(
|
||||
getVectorTypeId(pointer.type), sclass);
|
||||
|
||||
GcnRegisterPointer result;
|
||||
result.type = pointer.type;
|
||||
result.id = m_module.opAccessChain(
|
||||
ptrTypeId, pointer.id, 1, &index);
|
||||
return result;
|
||||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitBuildConstVecf32(
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float w,
|
||||
const GcnRegMask& writeMask)
|
||||
{
|
||||
// TODO refactor these functions into one single template
|
||||
|
@ -308,10 +930,10 @@ namespace sce::gcn
|
|||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitBuildConstVecu32(
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
uint32_t z,
|
||||
uint32_t w,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
uint32_t z,
|
||||
uint32_t w,
|
||||
const GcnRegMask& writeMask)
|
||||
{
|
||||
std::array<uint32_t, 4> ids = { 0, 0, 0, 0 };
|
||||
|
@ -338,10 +960,10 @@ namespace sce::gcn
|
|||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitBuildConstVeci32(
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t z,
|
||||
int32_t w,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t z,
|
||||
int32_t w,
|
||||
const GcnRegMask& writeMask)
|
||||
{
|
||||
std::array<uint32_t, 4> ids = { 0, 0, 0, 0 };
|
||||
|
@ -368,8 +990,8 @@ namespace sce::gcn
|
|||
}
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitBuildConstVecf64(
|
||||
double xy,
|
||||
double zw,
|
||||
double xy,
|
||||
double zw,
|
||||
const GcnRegMask& writeMask)
|
||||
{
|
||||
std::array<uint32_t, 2> ids = { 0, 0 };
|
||||
|
@ -390,7 +1012,6 @@ namespace sce::gcn
|
|||
: ids[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
GcnRegisterValue GcnCompiler::emitRegisterBitcast(
|
||||
GcnRegisterValue srcValue,
|
||||
|
@ -542,7 +1163,7 @@ namespace sce::gcn
|
|||
|
||||
GcnRegisterValue GcnCompiler::emitRegisterExtend(
|
||||
GcnRegisterValue value,
|
||||
uint32_t size)
|
||||
uint32_t size)
|
||||
{
|
||||
if (size == 1)
|
||||
return value;
|
||||
|
@ -628,7 +1249,7 @@ namespace sce::gcn
|
|||
|
||||
GcnRegisterValue GcnCompiler::emitRegisterMaskBits(
|
||||
GcnRegisterValue value,
|
||||
uint32_t mask)
|
||||
uint32_t mask)
|
||||
{
|
||||
GcnRegisterValue maskVector = emitBuildConstVecu32(
|
||||
mask, mask, mask, mask, GcnRegMask::firstN(value.type.ccount));
|
||||
|
@ -641,7 +1262,7 @@ namespace sce::gcn
|
|||
return result;
|
||||
}
|
||||
|
||||
//GcnRegisterValue GcnCompiler::emitSrcOperandModifiers(
|
||||
// GcnRegisterValue GcnCompiler::emitSrcOperandModifiers(
|
||||
// GcnRegisterValue value,
|
||||
// GcnRegModifiers modifiers)
|
||||
//{
|
||||
|
@ -653,7 +1274,7 @@ namespace sce::gcn
|
|||
// return value;
|
||||
//}
|
||||
|
||||
//GcnRegisterValue GcnCompiler::emitDstOperandModifiers(
|
||||
// GcnRegisterValue GcnCompiler::emitDstOperandModifiers(
|
||||
// GcnRegisterValue value,
|
||||
// GcnOpModifiers modifiers)
|
||||
//{
|
||||
|
@ -772,4 +1393,105 @@ namespace sce::gcn
|
|||
type == GcnScalarType::Float64;
|
||||
}
|
||||
|
||||
uint32_t GcnCompiler::getUserSgprCount() const
|
||||
{
|
||||
uint32_t count = 0;
|
||||
auto type = m_programInfo.type();
|
||||
// clang-format off
|
||||
switch (type)
|
||||
{
|
||||
case GcnProgramType::VertexShader: count = m_meta.vs.userSgprCount; break;
|
||||
case GcnProgramType::PixelShader: count = m_meta.ps.userSgprCount; break;
|
||||
case GcnProgramType::ComputeShader: count = m_meta.cs.userSgprCount; break;
|
||||
case GcnProgramType::GeometryShader:count = m_meta.gs.userSgprCount; break;
|
||||
case GcnProgramType::HullShader: count = m_meta.hs.userSgprCount; break;
|
||||
case GcnProgramType::DomainShader: count = m_meta.ds.userSgprCount; break;
|
||||
}
|
||||
// clang-format on
|
||||
return count;
|
||||
}
|
||||
|
||||
bool GcnCompiler::hasFetchShader() const
|
||||
{
|
||||
auto& resTable = m_header->getShaderResourceTable();
|
||||
auto iter = std::find_if(resTable.begin(), resTable.end(),
|
||||
[](const GcnShaderResource& res)
|
||||
{
|
||||
return res.usage == kShaderInputUsageSubPtrFetchShader;
|
||||
});
|
||||
return iter != resTable.end();
|
||||
}
|
||||
|
||||
std::pair<const VertexInputSemantic*, uint32_t>
|
||||
GcnCompiler::getSemanticTable()
|
||||
{
|
||||
const VertexInputSemantic* semanticTable = nullptr;
|
||||
uint32_t semanticCount = 0;
|
||||
|
||||
switch (m_programInfo.type())
|
||||
{
|
||||
case GcnProgramType::VertexShader:
|
||||
{
|
||||
semanticTable = m_meta.vs.inputSemanticTable;
|
||||
semanticCount = m_meta.vs.inputSemanticCount;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ASSERT(false, "program type not supported, please support it.");
|
||||
break;
|
||||
}
|
||||
|
||||
return std::make_pair(semanticTable, semanticCount);
|
||||
}
|
||||
|
||||
const std::array<GcnTextureInfo, 128>&
|
||||
GcnCompiler::getTextureInfoTable()
|
||||
{
|
||||
switch (m_programInfo.type())
|
||||
{
|
||||
case GcnProgramType::PixelShader:
|
||||
{
|
||||
return m_meta.ps.textureInfos;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ASSERT(false, "program type not supported, please support it.");
|
||||
}
|
||||
|
||||
GcnImageInfo GcnCompiler::getImageType(
|
||||
Gnm::TextureType textureType,
|
||||
bool isStorage,
|
||||
bool isDepth) const
|
||||
{
|
||||
uint32_t depth = isDepth ? 1u : 0u;
|
||||
uint32_t sampled = isStorage ? 2u : 1u;
|
||||
GcnImageInfo typeInfo = [textureType, depth, sampled]() -> GcnImageInfo
|
||||
{
|
||||
switch (textureType)
|
||||
{
|
||||
case Gnm::kTextureType1d:
|
||||
return { spv::Dim1D, depth, 0, 0, sampled, VK_IMAGE_VIEW_TYPE_1D };
|
||||
case Gnm::kTextureType2d:
|
||||
return { spv::Dim2D, depth, 0, 0, sampled, VK_IMAGE_VIEW_TYPE_2D };
|
||||
case Gnm::kTextureType3d:
|
||||
return { spv::Dim3D, depth, 0, 0, sampled, VK_IMAGE_VIEW_TYPE_3D };
|
||||
case Gnm::kTextureTypeCubemap:
|
||||
return { spv::DimCube, depth, 0, 0, sampled, VK_IMAGE_VIEW_TYPE_CUBE };
|
||||
case Gnm::kTextureType1dArray:
|
||||
return { spv::Dim1D, depth, 1, 0, sampled, VK_IMAGE_VIEW_TYPE_1D_ARRAY };
|
||||
case Gnm::kTextureType2dArray:
|
||||
return { spv::Dim2D, depth, 1, 0, sampled, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
||||
case Gnm::kTextureType2dMsaa:
|
||||
return { spv::Dim2D, depth, 0, 1, sampled, VK_IMAGE_VIEW_TYPE_2D };
|
||||
case Gnm::kTextureType2dArrayMsaa:
|
||||
return { spv::Dim2D, depth, 1, 1, sampled, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
||||
default:
|
||||
Logger::exception(util::str::formatex("GcnCompiler: Unsupported resource type: ", textureType));
|
||||
}
|
||||
}();
|
||||
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
|
||||
} // namespace sce::gcn
|
|
@ -5,6 +5,7 @@
|
|||
#include "GcnInstructionIterator.h"
|
||||
#include "GcnShaderMeta.h"
|
||||
#include "GcnDecoder.h"
|
||||
#include "GcnCompilerDefs.h"
|
||||
|
||||
#include "SpirV/SpirvModule.h"
|
||||
#include "Violet/VltRc.h"
|
||||
|
@ -13,6 +14,10 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
namespace sce::Gnm
|
||||
{
|
||||
enum TextureType;
|
||||
} // namespace sce::Gnm
|
||||
|
||||
namespace sce::gcn
|
||||
{
|
||||
|
@ -20,150 +25,8 @@ namespace sce::gcn
|
|||
class GcnHeader;
|
||||
struct GcnShaderInstruction;
|
||||
struct GcnAnalysisInfo;
|
||||
struct GcnShaderResource;
|
||||
|
||||
enum class GcnZeroTest : uint32_t
|
||||
{
|
||||
TestZ = 0,
|
||||
TestNz = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Scalar value type
|
||||
*
|
||||
* Enumerates possible register component
|
||||
* types. Scalar types are represented as
|
||||
* a one-component vector type.
|
||||
*/
|
||||
enum class GcnScalarType : uint32_t
|
||||
{
|
||||
Uint32 = 0,
|
||||
Uint64 = 1,
|
||||
Sint32 = 2,
|
||||
Sint64 = 3,
|
||||
Float32 = 4,
|
||||
Float64 = 5,
|
||||
Bool = 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Vector type
|
||||
*
|
||||
* Convenience struct that stores a scalar
|
||||
* type and a component count. The compiler
|
||||
* can use this to generate SPIR-V types.
|
||||
*/
|
||||
struct GcnVectorType
|
||||
{
|
||||
GcnScalarType ctype;
|
||||
uint32_t ccount;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Array type
|
||||
*
|
||||
* Convenience struct that stores a scalar type, a
|
||||
* component count and an array size. An array of
|
||||
* length 0 will be evaluated to a vector type. The
|
||||
* compiler can use this to generate SPIR-V types.
|
||||
*/
|
||||
struct GcnArrayType
|
||||
{
|
||||
GcnScalarType ctype;
|
||||
uint32_t ccount;
|
||||
uint32_t alength;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Register info
|
||||
*
|
||||
* Stores the array type of a register and
|
||||
* its storage class. The compiler can use
|
||||
* this to generate SPIR-V pointer types.
|
||||
*/
|
||||
struct GcnRegisterInfo
|
||||
{
|
||||
GcnArrayType type;
|
||||
spv::StorageClass sclass;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Register value
|
||||
*
|
||||
* Stores a vector type and a SPIR-V ID that
|
||||
* represents an intermediate value. This is
|
||||
* used to track the type of such values.
|
||||
*/
|
||||
struct GcnRegisterValue
|
||||
{
|
||||
GcnVectorType type;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Register pointer
|
||||
*
|
||||
* Stores a vector type and a SPIR-V ID that
|
||||
* represents a pointer to such a vector. This
|
||||
* can be used to load registers conveniently.
|
||||
*/
|
||||
struct GcnRegisterPointer
|
||||
{
|
||||
GcnVectorType type;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Vertex shader-specific structure
|
||||
*/
|
||||
struct GcnCompilerVsPart
|
||||
{
|
||||
uint32_t functionId = 0;
|
||||
|
||||
uint32_t builtinVertexId = 0;
|
||||
uint32_t builtinInstanceId = 0;
|
||||
uint32_t builtinBaseVertex = 0;
|
||||
uint32_t builtinBaseInstance = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Pixel shader-specific structure
|
||||
*/
|
||||
struct GcnCompilerPsPart
|
||||
{
|
||||
uint32_t functionId = 0;
|
||||
|
||||
uint32_t builtinFragCoord = 0;
|
||||
uint32_t builtinDepth = 0;
|
||||
uint32_t builtinStencilRef = 0;
|
||||
uint32_t builtinIsFrontFace = 0;
|
||||
uint32_t builtinSampleId = 0;
|
||||
uint32_t builtinSampleMaskIn = 0;
|
||||
uint32_t builtinSampleMaskOut = 0;
|
||||
uint32_t builtinLayer = 0;
|
||||
uint32_t builtinViewportId = 0;
|
||||
|
||||
uint32_t builtinLaneId = 0;
|
||||
uint32_t killState = 0;
|
||||
|
||||
uint32_t specRsSampleCount = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Compute shader-specific structure
|
||||
*/
|
||||
struct GcnCompilerCsPart
|
||||
{
|
||||
uint32_t functionId = 0;
|
||||
|
||||
uint32_t workgroupSizeX = 0;
|
||||
uint32_t workgroupSizeY = 0;
|
||||
uint32_t workgroupSizeZ = 0;
|
||||
|
||||
uint32_t builtinGlobalInvocationId = 0;
|
||||
uint32_t builtinLocalInvocationId = 0;
|
||||
uint32_t builtinLocalInvocationIndex = 0;
|
||||
uint32_t builtinWorkgroupId = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Shader recompiler
|
||||
|
@ -243,12 +106,86 @@ namespace sce::gcn
|
|||
void emitPsFinalize();
|
||||
void emitCsFinalize();
|
||||
|
||||
private:
|
||||
////////////////////////////
|
||||
// Input/output preparation
|
||||
void emitInputFetch();
|
||||
void emitInputSetup();
|
||||
void emitFetchInput();
|
||||
|
||||
void emitOutputSetup();
|
||||
/////////////////////////////////////////////////////
|
||||
// Shader interface and metadata declaration methods
|
||||
void emitDclInputSlots();
|
||||
void emitDclVertexInput();
|
||||
void emitDclInput(
|
||||
const VertexInputSemantic& sema);
|
||||
|
||||
void emitDclBuffer(
|
||||
const GcnShaderResource& res);
|
||||
void emitDclTexture(
|
||||
const GcnShaderResource& res);
|
||||
void emitDclSampler(
|
||||
const GcnShaderResource& res);
|
||||
///////////////////////////////
|
||||
// Variable definition methods
|
||||
uint32_t emitNewVariable(
|
||||
const GcnRegisterInfo& info);
|
||||
|
||||
uint32_t emitNewBuiltinVariable(
|
||||
const GcnRegisterInfo& info,
|
||||
spv::BuiltIn builtIn,
|
||||
const char* name);
|
||||
|
||||
///////////////////////////////
|
||||
// SGPR/VGPR load/store methods
|
||||
GcnRegisterValue emitVgprLoad(
|
||||
const GcnInstOperand& reg);
|
||||
GcnRegisterValue emitSgprLoad(
|
||||
const GcnInstOperand& reg);
|
||||
GcnRegisterValue emitVgprArrayLoad(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count);
|
||||
GcnRegisterValue emitSgprArrayLoad(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count);
|
||||
|
||||
void emitVgprStore(
|
||||
const GcnInstOperand& reg,
|
||||
const GcnRegisterValue& value);
|
||||
void emitSgprStore(
|
||||
const GcnInstOperand& reg,
|
||||
const GcnRegisterValue& value);
|
||||
void emitVgprArrayStore(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count,
|
||||
const GcnRegisterValue& value);
|
||||
void emitSgprArrayStore(
|
||||
const GcnInstOperand& start,
|
||||
uint32_t count,
|
||||
const GcnRegisterValue& value);
|
||||
|
||||
//////////////////////////////
|
||||
// Operand load/store methods
|
||||
GcnRegisterValue emitValueLoad(
|
||||
GcnRegisterPointer ptr);
|
||||
|
||||
void emitValueStore(
|
||||
GcnRegisterPointer ptr,
|
||||
GcnRegisterValue value,
|
||||
GcnRegMask writeMask);
|
||||
|
||||
GcnRegisterValue emitRegisterLoad(
|
||||
const GcnInstOperand& reg,
|
||||
GcnRegMask writeMask);
|
||||
|
||||
void emitRegisterStore(
|
||||
const GcnInstOperand& reg,
|
||||
GcnRegisterValue value);
|
||||
|
||||
GcnRegisterPointer emitCompositeAccess(
|
||||
GcnRegisterPointer pointer,
|
||||
spv::StorageClass sclass,
|
||||
uint32_t index);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Constant building methods. These are used to
|
||||
// generate constant vectors that store the same
|
||||
|
@ -327,6 +264,7 @@ namespace sce::gcn
|
|||
//GcnRegisterValue emitDstOperandModifiers(
|
||||
// GcnRegisterValue value,
|
||||
// GcnOpModifiers modifiers);
|
||||
|
||||
///////////////////////////
|
||||
// Type definition methods
|
||||
uint32_t getScalarTypeId(
|
||||
|
@ -343,11 +281,27 @@ namespace sce::gcn
|
|||
|
||||
uint32_t getPerVertexBlockId();
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
bool isDoubleType(
|
||||
GcnScalarType type) const;
|
||||
|
||||
uint32_t getUserSgprCount() const;
|
||||
|
||||
bool hasFetchShader() const;
|
||||
|
||||
std::pair<const VertexInputSemantic*, uint32_t>
|
||||
getSemanticTable();
|
||||
|
||||
const std::array<GcnTextureInfo, 128>&
|
||||
getTextureInfoTable();
|
||||
|
||||
GcnImageInfo getImageType(
|
||||
Gnm::TextureType textureType,
|
||||
bool isStorage,
|
||||
bool isDepth) const;
|
||||
|
||||
private:
|
||||
GcnProgramInfo m_programInfo;
|
||||
const GcnHeader* m_header;
|
||||
|
@ -361,10 +315,36 @@ namespace sce::gcn
|
|||
uint32_t m_entryPointId = 0;
|
||||
std::vector<uint32_t> m_entryPointInterfaces;
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// Shader input interfaces
|
||||
std::array<
|
||||
GcnRegisterPointer,
|
||||
GcnMaxInterfaceRegs> m_inputs;
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// VGPR/SGPR registers
|
||||
std::array<
|
||||
GcnRegisterPointer,
|
||||
GcnMaxVGPR> m_vgprs = {};
|
||||
std::array<
|
||||
GcnRegisterPointer,
|
||||
GcnMaxSGPR> m_sgprs = {};
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Shader resource variables. These provide access to
|
||||
// buffers, samplers and textures.
|
||||
std::array<GcnBuffer, 16> m_buffers;
|
||||
std::array<GcnSampler, 16> m_samplers;
|
||||
std::array<GcnTexture, 128> m_textures;
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// Function state tracking. Required in order
|
||||
// to properly end functions in some cases.
|
||||
bool m_insideFunction = false;
|
||||
///////////////////////////////////////////////////////////
|
||||
// An array stores up to 16 user data registers.
|
||||
uint32_t m_vUserDataArray = 0;
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Per-vertex input and output blocks. Depending on
|
||||
// the shader stage, these may be declared as arrays.
|
||||
|
|
211
GPCS4/Graphics/Gcn/GcnCompilerDefs.h
Normal file
211
GPCS4/Graphics/Gcn/GcnCompilerDefs.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
#pragma once
|
||||
|
||||
#include "GcnCommon.h"
|
||||
|
||||
namespace sce::gcn
|
||||
{
|
||||
constexpr size_t GcnMaxInterfaceRegs = 32;
|
||||
constexpr size_t GcnMaxSGPR = 104;
|
||||
constexpr size_t GcnMaxVGPR = 256;
|
||||
constexpr size_t GcnCodeVGPR0 = 256;
|
||||
|
||||
enum class GcnZeroTest : uint32_t
|
||||
{
|
||||
TestZ = 0,
|
||||
TestNz = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Scalar value type
|
||||
*
|
||||
* Enumerates possible register component
|
||||
* types. Scalar types are represented as
|
||||
* a one-component vector type.
|
||||
*/
|
||||
enum class GcnScalarType : uint32_t
|
||||
{
|
||||
Uint32 = 0,
|
||||
Uint64 = 1,
|
||||
Sint32 = 2,
|
||||
Sint64 = 3,
|
||||
Float32 = 4,
|
||||
Float64 = 5,
|
||||
Bool = 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Vector type
|
||||
*
|
||||
* Convenience struct that stores a scalar
|
||||
* type and a component count. The compiler
|
||||
* can use this to generate SPIR-V types.
|
||||
*/
|
||||
struct GcnVectorType
|
||||
{
|
||||
GcnScalarType ctype;
|
||||
uint32_t ccount;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Array type
|
||||
*
|
||||
* Convenience struct that stores a scalar type, a
|
||||
* component count and an array size. An array of
|
||||
* length 0 will be evaluated to a vector type. The
|
||||
* compiler can use this to generate SPIR-V types.
|
||||
*/
|
||||
struct GcnArrayType
|
||||
{
|
||||
GcnScalarType ctype;
|
||||
uint32_t ccount;
|
||||
uint32_t alength;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Register info
|
||||
*
|
||||
* Stores the array type of a register and
|
||||
* its storage class. The compiler can use
|
||||
* this to generate SPIR-V pointer types.
|
||||
*/
|
||||
struct GcnRegisterInfo
|
||||
{
|
||||
GcnArrayType type;
|
||||
spv::StorageClass sclass;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Register value
|
||||
*
|
||||
* Stores a vector type and a SPIR-V ID that
|
||||
* represents an intermediate value. This is
|
||||
* used to track the type of such values.
|
||||
*/
|
||||
struct GcnRegisterValue
|
||||
{
|
||||
GcnVectorType type;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Register pointer
|
||||
*
|
||||
* Stores a vector type and a SPIR-V ID that
|
||||
* represents a pointer to such a vector. This
|
||||
* can be used to load registers conveniently.
|
||||
*/
|
||||
struct GcnRegisterPointer
|
||||
{
|
||||
GcnVectorType type;
|
||||
uint32_t id = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Vertex shader-specific structure
|
||||
*/
|
||||
struct GcnCompilerVsPart
|
||||
{
|
||||
uint32_t functionId = 0;
|
||||
uint32_t fetchFuncId = 0;
|
||||
|
||||
uint32_t builtinVertexId = 0;
|
||||
uint32_t builtinInstanceId = 0;
|
||||
uint32_t builtinBaseVertex = 0;
|
||||
uint32_t builtinBaseInstance = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Pixel shader-specific structure
|
||||
*/
|
||||
struct GcnCompilerPsPart
|
||||
{
|
||||
uint32_t functionId = 0;
|
||||
|
||||
uint32_t builtinFragCoord = 0;
|
||||
uint32_t builtinDepth = 0;
|
||||
uint32_t builtinStencilRef = 0;
|
||||
uint32_t builtinIsFrontFace = 0;
|
||||
uint32_t builtinSampleId = 0;
|
||||
uint32_t builtinSampleMaskIn = 0;
|
||||
uint32_t builtinSampleMaskOut = 0;
|
||||
uint32_t builtinLayer = 0;
|
||||
uint32_t builtinViewportId = 0;
|
||||
|
||||
uint32_t builtinLaneId = 0;
|
||||
uint32_t killState = 0;
|
||||
|
||||
uint32_t specRsSampleCount = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Compute shader-specific structure
|
||||
*/
|
||||
struct GcnCompilerCsPart
|
||||
{
|
||||
uint32_t functionId = 0;
|
||||
|
||||
uint32_t workgroupSizeX = 0;
|
||||
uint32_t workgroupSizeY = 0;
|
||||
uint32_t workgroupSizeZ = 0;
|
||||
|
||||
uint32_t builtinGlobalInvocationId = 0;
|
||||
uint32_t builtinLocalInvocationId = 0;
|
||||
uint32_t builtinLocalInvocationIndex = 0;
|
||||
uint32_t builtinWorkgroupId = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Image type information
|
||||
*/
|
||||
struct GcnImageInfo
|
||||
{
|
||||
spv::Dim dim = spv::Dim1D;
|
||||
uint32_t depth = 0;
|
||||
uint32_t array = 0;
|
||||
uint32_t ms = 0;
|
||||
uint32_t sampled = 0;
|
||||
|
||||
VkImageViewType vtype = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Constant buffer binding
|
||||
*
|
||||
* Stores information for a V#
|
||||
*/
|
||||
struct GcnBuffer
|
||||
{
|
||||
uint32_t varId = 0;
|
||||
uint32_t size = 0;
|
||||
bool asSsbo = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sampler binding
|
||||
*
|
||||
* Stores information for a S#
|
||||
*/
|
||||
struct GcnSampler
|
||||
{
|
||||
uint32_t varId = 0;
|
||||
uint32_t typeId = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Shader resource binding
|
||||
*
|
||||
* Stores information for a T#
|
||||
*/
|
||||
struct GcnTexture
|
||||
{
|
||||
uint32_t varId = 0;
|
||||
GcnImageInfo imageInfo = {};
|
||||
GcnScalarType sampledType = GcnScalarType::Float32;
|
||||
uint32_t sampledTypeId = 0;
|
||||
uint32_t imageTypeId = 0;
|
||||
uint32_t colorTypeId = 0;
|
||||
uint32_t depthTypeId = 0;
|
||||
};
|
||||
|
||||
} // namespace sce::gcn
|
|
@ -21,7 +21,7 @@ namespace sce::gcn
|
|||
|
||||
enum GcnLimits : uint32_t
|
||||
{
|
||||
kMaxResourceCount = 16, ///< PSSL compiler limit is 128, Default value is 16
|
||||
kMaxResourceCount = 128, ///< PSSL compiler limit is 128, Default value is 16
|
||||
kMaxRwResourceCount = 16, ///< PSSL compiler limit is 16, Default value is 16
|
||||
kMaxSamplerCount = 16, ///< PSSL compiler limit is 16, Default value is 16
|
||||
kMaxVertexBufferCount = 32, ///< PSSL compiler limit is 32, Default value is 16
|
||||
|
|
|
@ -2432,6 +2432,4 @@ namespace sce::gcn
|
|||
Undefined = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace sce::gcn
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#include "GcnCommon.h"
|
||||
#include "GcnShaderBinary.h"
|
||||
#include "GcnConstants.h"
|
||||
#include "Gnm/GnmConstant.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace sce::gcn
|
||||
{
|
||||
|
@ -61,24 +64,53 @@ namespace sce::gcn
|
|||
GcnXfbInfo* xfb;
|
||||
};
|
||||
|
||||
struct GcnTextureInfo
|
||||
{
|
||||
Gnm::TextureType textureType;
|
||||
Gnm::TextureChannelType channelType;
|
||||
bool isDepth;
|
||||
};
|
||||
|
||||
struct GcnMetaVS
|
||||
{
|
||||
uint32_t userSgprCount;
|
||||
|
||||
uint32_t inputSemanticCount;
|
||||
VertexInputSemantic inputSemanticTable[kMaxVertexBufferCount];
|
||||
};
|
||||
|
||||
struct GcnMetaPS
|
||||
{
|
||||
uint32_t inputSemanticCount; // ps input semantic count
|
||||
uint32_t userSgprCount;
|
||||
|
||||
uint32_t inputSemanticCount; // ps input semantic count
|
||||
std::array<GcnTextureInfo, 128> textureInfos;
|
||||
};
|
||||
|
||||
struct GcnMetaCS
|
||||
{
|
||||
uint32_t userSgprCount;
|
||||
|
||||
uint32_t computeNumThreadX;
|
||||
uint32_t computeNumThreadY;
|
||||
uint32_t computeNumThreadZ;
|
||||
};
|
||||
|
||||
struct GcnMetaGS
|
||||
{
|
||||
uint32_t userSgprCount;
|
||||
};
|
||||
|
||||
struct GcnMetaHS
|
||||
{
|
||||
uint32_t userSgprCount;
|
||||
};
|
||||
|
||||
struct GcnMetaDS
|
||||
{
|
||||
uint32_t userSgprCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Shader meta information
|
||||
*
|
||||
|
@ -93,6 +125,9 @@ namespace sce::gcn
|
|||
GcnMetaVS vs;
|
||||
GcnMetaPS ps;
|
||||
GcnMetaCS cs;
|
||||
GcnMetaGS gs;
|
||||
GcnMetaHS hs;
|
||||
GcnMetaDS ds;
|
||||
};
|
||||
|
||||
} // namespace sce::gcn
|
|
@ -793,7 +793,8 @@ namespace sce::Gnm
|
|||
bindings[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
// Fix element count
|
||||
sema.m_sizeInElements = vsharp->getDataFormat().getNumComponents();
|
||||
sema.m_sizeInElements =
|
||||
std::min(static_cast<uint32_t>(sema.m_sizeInElements), vsharp->getDataFormat().getNumComponents());
|
||||
}
|
||||
|
||||
m_context->setInputLayout(
|
||||
|
@ -874,7 +875,7 @@ namespace sce::Gnm
|
|||
auto& resTable = psModule.getResourceTable();
|
||||
|
||||
// create and bind shader resources
|
||||
bindResource(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, resTable, ctx.userData);
|
||||
bindResource(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, resTable, ctx.userData);
|
||||
|
||||
// bind the shader
|
||||
m_context->bindShader(
|
||||
|
@ -922,26 +923,38 @@ namespace sce::Gnm
|
|||
info.usage = usage;
|
||||
info.stage = stage;
|
||||
info.access = access;
|
||||
info.memoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
m_factory.createBuffer(info, buffer);
|
||||
m_tracker->track(buffer);
|
||||
|
||||
m_context->uploadBuffer(buffer.buffer,
|
||||
buffer.gnmBuffer.getBaseAddress());
|
||||
|
||||
uint32_t slot = 0;
|
||||
if (usage == VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
|
||||
{
|
||||
info.memoryType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
|
||||
m_factory.createBuffer(info, buffer);
|
||||
|
||||
void* bufferMem = buffer.buffer->mapPtr(0);
|
||||
std::memcpy(bufferMem,
|
||||
buffer.gnmBuffer.getBaseAddress(),
|
||||
buffer.gnmBuffer.getSize());
|
||||
|
||||
slot = computeConstantBufferBinding(
|
||||
gcnProgramTypeFromVkStage(stage), startRegister);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.memoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
m_factory.createBuffer(info, buffer);
|
||||
|
||||
m_context->uploadBuffer(buffer.buffer,
|
||||
buffer.gnmBuffer.getBaseAddress());
|
||||
|
||||
slot = computeResourceBinding(
|
||||
gcnProgramTypeFromVkStage(stage), startRegister);
|
||||
}
|
||||
|
||||
m_tracker->track(buffer);
|
||||
|
||||
m_context->bindResourceBuffer(slot, VltBufferSlice(buffer.buffer));
|
||||
}
|
||||
|
||||
|
@ -1065,6 +1078,9 @@ namespace sce::Gnm
|
|||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
updateMetaTextureInfo(stage, res.startRegister, false, tsharp);
|
||||
|
||||
}
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
|
@ -1079,6 +1095,8 @@ namespace sce::Gnm
|
|||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_GENERAL);
|
||||
|
||||
updateMetaTextureInfo(stage, res.startRegister, false, tsharp);
|
||||
}
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
|
@ -1168,6 +1186,60 @@ namespace sce::Gnm
|
|||
}
|
||||
}
|
||||
|
||||
ShaderStage GnmCommandBufferDraw::getShaderStage(
|
||||
VkPipelineStageFlags pipeStage)
|
||||
{
|
||||
ShaderStage shaderStage = kShaderStageCount;
|
||||
// clang-format off
|
||||
switch (pipeStage)
|
||||
{
|
||||
case VK_PIPELINE_STAGE_VERTEX_SHADER_BIT: shaderStage = kShaderStageVs; break;
|
||||
case VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT: shaderStage = kShaderStagePs; break;
|
||||
case VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT: shaderStage = kShaderStageCs; break;
|
||||
case VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT: shaderStage = kShaderStageGs; break;
|
||||
case VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT: shaderStage = kShaderStageHs; break;
|
||||
// Don't know which stage equals domain, and what the fuck is ES LS?
|
||||
//case VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT: shaderStage = kShaderStageEs; break;
|
||||
default:
|
||||
LOG_ASSERT(false, "pipeline stage can not be converted.");
|
||||
break;
|
||||
}
|
||||
// clang-format on
|
||||
return shaderStage;
|
||||
}
|
||||
|
||||
void GnmCommandBufferDraw::updateMetaTextureInfo(
|
||||
VkPipelineStageFlags stage,
|
||||
uint32_t startRegister,
|
||||
bool isDepth,
|
||||
const Texture* tsharp)
|
||||
{
|
||||
// T# information is ripped upon uploading shader binary to GPU,
|
||||
// yet we need these information to proper declare image resource
|
||||
// when recompiling shaders.
|
||||
|
||||
GcnTextureInfo info;
|
||||
info.textureType = tsharp->getTextureType();
|
||||
info.channelType = tsharp->getTextureChannelType();
|
||||
info.isDepth = isDepth;
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT:
|
||||
{
|
||||
auto& ctx = m_state.shaderContext[kShaderStagePs];
|
||||
ctx.meta.ps.textureInfos[startRegister] = info;
|
||||
}
|
||||
break;
|
||||
case VK_PIPELINE_STAGE_VERTEX_SHADER_BIT:
|
||||
case VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT:
|
||||
case VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT:
|
||||
case VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT:
|
||||
case VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT:
|
||||
default:
|
||||
LOG_ASSERT(false, "TODO: stage %d is not supported yet, please support it.", stage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sce::Gnm
|
|
@ -198,6 +198,15 @@ namespace sce::Gnm
|
|||
|
||||
void onPrepareFlip();
|
||||
|
||||
ShaderStage getShaderStage(
|
||||
VkPipelineStageFlags pipeStage);
|
||||
|
||||
void updateMetaTextureInfo(
|
||||
VkPipelineStageFlags stage,
|
||||
uint32_t startRegister,
|
||||
bool isDepth,
|
||||
const Texture* tsharp);
|
||||
|
||||
private:
|
||||
GnmGraphicsState m_state;
|
||||
GnmContextFlags m_flags;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "GnmConverter.h"
|
||||
#include "SpirV/spirv.hpp"
|
||||
|
||||
using namespace sce::vlt;
|
||||
|
||||
|
@ -407,4 +408,32 @@ namespace sce::Gnm::cvt
|
|||
return op;
|
||||
}
|
||||
|
||||
spv::Dim convertTextureTypeToDim(TextureType textureType)
|
||||
{
|
||||
using namespace spv;
|
||||
|
||||
Dim dim = DimMax;
|
||||
|
||||
// clang-format off
|
||||
|
||||
switch (textureType)
|
||||
{
|
||||
case Gnm::kTextureType1d: dim = Dim1D; break;
|
||||
case Gnm::kTextureType2d: dim = Dim2D; break;
|
||||
case Gnm::kTextureType3d: dim = Dim3D; break;
|
||||
case Gnm::kTextureTypeCubemap: dim = DimCube; break;
|
||||
case Gnm::kTextureType1dArray: dim = Dim1D; break;
|
||||
case Gnm::kTextureType2dArray: dim = Dim2D; break;
|
||||
case Gnm::kTextureType2dMsaa: dim = Dim2D; break;
|
||||
case Gnm::kTextureType2dArrayMsaa: dim = Dim2D; break;
|
||||
default:
|
||||
LOG_ASSERT(false, "texture type not supported.");
|
||||
break;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
} // namespace sce::Gnm::cvt
|
|
@ -8,6 +8,11 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
namespace spv
|
||||
{
|
||||
enum Dim;
|
||||
} // namespace spv
|
||||
|
||||
namespace sce::Gnm::cvt
|
||||
{
|
||||
VkFormat convertZFormat(ZFormat zfmt);
|
||||
|
@ -50,4 +55,6 @@ namespace sce::Gnm::cvt
|
|||
|
||||
VkCompareOp convertDepthCompare(DepthCompare compare);
|
||||
|
||||
spv::Dim convertTextureTypeToDim(TextureType textureType);
|
||||
|
||||
} // namespace sce::Gnm
|
51
Misc/MultipleBufferBindingPoints.cpp
Normal file
51
Misc/MultipleBufferBindingPoints.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
Shader
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec3 inColor;
|
||||
layout (location = 2) in vec3 inPosB;
|
||||
layout (location = 3) in vec3 inColorB;
|
||||
|
||||
...
|
||||
|
||||
Application
|
||||
|
||||
// Input state
|
||||
std::array<VkVertexInputBindingDescription,2> vertexInputBinding = {};
|
||||
vertexInputBinding[0].binding = 0;
|
||||
vertexInputBinding[0].stride = sizeof(Vertex);
|
||||
vertexInputBinding[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
vertexInputBinding[1].binding = 1;
|
||||
vertexInputBinding[1].stride = sizeof(Vertex);
|
||||
vertexInputBinding[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
// Inpute attribute bindings describe shader attribute locations and memory layouts
|
||||
std::array<VkVertexInputAttributeDescription, 4> vertexInputAttributs;
|
||||
vertexInputAttributs[0].binding = 0;
|
||||
vertexInputAttributs[0].location = 0;
|
||||
vertexInputAttributs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
vertexInputAttributs[0].offset = offsetof(Vertex, position);
|
||||
vertexInputAttributs[1].binding = 0;
|
||||
vertexInputAttributs[1].location = 1;
|
||||
vertexInputAttributs[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
vertexInputAttributs[1].offset = offsetof(Vertex, color);
|
||||
|
||||
vertexInputAttributs[2].binding = 1;
|
||||
vertexInputAttributs[2].location = 2;
|
||||
vertexInputAttributs[2].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
vertexInputAttributs[2].offset = offsetof(Vertex, position);
|
||||
vertexInputAttributs[3].binding = 1;
|
||||
vertexInputAttributs[3].location = 3;
|
||||
vertexInputAttributs[3].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
vertexInputAttributs[3].offset = offsetof(Vertex, color);
|
||||
|
||||
// Vertex input state used for pipeline creation
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState = {};
|
||||
vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertexInputState.vertexBindingDescriptionCount = 2;
|
||||
vertexInputState.pVertexBindingDescriptions = vertexInputBinding.data();
|
||||
vertexInputState.vertexAttributeDescriptionCount = 4;
|
||||
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributs.data();
|
||||
|
||||
// Bind
|
||||
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertices.buffer, offsets);
|
||||
vkCmdBindVertexBuffers(drawCmdBuffers[i], 1, 1, &verticesB.buffer, offsets);
|
Loading…
Reference in a new issue