mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-11 19:54:14 +01:00
[dxvk] Restructured state tracker again
This commit is contained in:
parent
b367f6af55
commit
a84e2eabc2
@ -37,98 +37,19 @@ namespace dxvk {
|
|||||||
|
|
||||||
virtual ~DxbcCodeGen();
|
virtual ~DxbcCodeGen();
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Declares temporary registers
|
|
||||||
* \param [in] n Number of temp registers
|
|
||||||
*/
|
|
||||||
void dclTemps(uint32_t n);
|
void dclTemps(uint32_t n);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Declares an interface variable
|
|
||||||
*
|
|
||||||
* \param [in] regType Register type
|
|
||||||
* \param [in] regId Interface register index
|
|
||||||
* \param [in] regDim Array dimension of interface variable
|
|
||||||
* \param [in] regMask Component mask for this declaration
|
|
||||||
* \param [in] sv System value to map to the given components
|
|
||||||
*/
|
|
||||||
virtual void dclInterfaceVar(
|
|
||||||
DxbcOperandType regType,
|
|
||||||
uint32_t regId,
|
|
||||||
uint32_t regDim,
|
|
||||||
DxbcComponentMask regMask,
|
|
||||||
DxbcSystemValue sv) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Defines 32-bit constant
|
|
||||||
*
|
|
||||||
* The constant will be declared as a 32-bit
|
|
||||||
* unsigned integer. Cast the resulting value
|
|
||||||
* to the required type.
|
|
||||||
* \param [in] v Constant value
|
|
||||||
* \returns The constant value ID
|
|
||||||
*/
|
|
||||||
DxbcValue defConstScalar(uint32_t v);
|
DxbcValue defConstScalar(uint32_t v);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Defines 32-bit constant vector
|
|
||||||
*
|
|
||||||
* Defines a four-component vector of 32-bit
|
|
||||||
* unsigned integer values. Cast the resulting
|
|
||||||
* value to the required type as needed.
|
|
||||||
* \param [in] x First vector component
|
|
||||||
* \param [in] y Second vector component
|
|
||||||
* \param [in] z Third vector component
|
|
||||||
* \param [in] w Fourth vector component
|
|
||||||
* \returns The constant value ID
|
|
||||||
*/
|
|
||||||
DxbcValue defConstVector(
|
DxbcValue defConstVector(
|
||||||
uint32_t x, uint32_t y,
|
uint32_t x, uint32_t y,
|
||||||
uint32_t z, uint32_t w);
|
uint32_t z, uint32_t w);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns from function
|
|
||||||
*/
|
|
||||||
void fnReturn();
|
void fnReturn();
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Retrieves temporary register pointer
|
|
||||||
*
|
|
||||||
* Provides access to a temporary register.
|
|
||||||
* \param [in] regId Register index
|
|
||||||
* \returns Register pointer
|
|
||||||
*/
|
|
||||||
DxbcPointer ptrTempReg(
|
DxbcPointer ptrTempReg(
|
||||||
uint32_t regId);
|
uint32_t regId);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Pointer to an interface variable
|
|
||||||
*
|
|
||||||
* Provides access to an interface variable.
|
|
||||||
* \param [in] regType Register type
|
|
||||||
* \param [in] regId Register index
|
|
||||||
* \returns Register pointer
|
|
||||||
*/
|
|
||||||
virtual DxbcPointer ptrInterfaceVar(
|
|
||||||
DxbcOperandType regType,
|
|
||||||
uint32_t regId) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Pointer to an interface variable
|
|
||||||
*
|
|
||||||
* Provides access to an indexed interface variable.
|
|
||||||
* Some shader types may have indexed input or output
|
|
||||||
* variables that can be accesswed via an array index.
|
|
||||||
* \param [in] regType Register type
|
|
||||||
* \param [in] regId Register index
|
|
||||||
* \param [in] index Array index
|
|
||||||
* \returns Register pointer
|
|
||||||
*/
|
|
||||||
virtual DxbcPointer ptrInterfaceVarIndexed(
|
|
||||||
DxbcOperandType regType,
|
|
||||||
uint32_t regId,
|
|
||||||
const DxbcValue& index) = 0;
|
|
||||||
|
|
||||||
DxbcValue opAbs(
|
DxbcValue opAbs(
|
||||||
const DxbcValue& src);
|
const DxbcValue& src);
|
||||||
|
|
||||||
@ -146,99 +67,50 @@ namespace dxvk {
|
|||||||
DxbcValue opSaturate(
|
DxbcValue opSaturate(
|
||||||
const DxbcValue& src);
|
const DxbcValue& src);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Casts register value to another type
|
|
||||||
*
|
|
||||||
* Type cast that does not change the bit pattern
|
|
||||||
* of the value. This is required as DXBC values
|
|
||||||
* are not statically typed, but SPIR-V is.
|
|
||||||
* \param [in] src Source value
|
|
||||||
* \param [in] type Destination type
|
|
||||||
* \returns Resulting register value
|
|
||||||
*/
|
|
||||||
DxbcValue regCast(
|
DxbcValue regCast(
|
||||||
const DxbcValue& src,
|
const DxbcValue& src,
|
||||||
const DxbcValueType& type);
|
const DxbcValueType& type);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Extracts vector components
|
|
||||||
*
|
|
||||||
* Extracts the given set of components.
|
|
||||||
* \param [in] src Source vector
|
|
||||||
* \param [in] mask Component mask
|
|
||||||
* \returns Resulting register value
|
|
||||||
*/
|
|
||||||
DxbcValue regExtract(
|
DxbcValue regExtract(
|
||||||
const DxbcValue& src,
|
const DxbcValue& src,
|
||||||
DxbcComponentMask mask);
|
DxbcComponentMask mask);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Swizzles a vector register
|
|
||||||
*
|
|
||||||
* Swizzles the vector and extracts
|
|
||||||
* the given set of vector components.
|
|
||||||
* \param [in] src Source vector to swizzle
|
|
||||||
* \param [in] swizzle The component swizzle
|
|
||||||
* \param [in] mask Components to extract
|
|
||||||
* \returns Resulting register value
|
|
||||||
*/
|
|
||||||
DxbcValue regSwizzle(
|
DxbcValue regSwizzle(
|
||||||
const DxbcValue& src,
|
const DxbcValue& src,
|
||||||
const DxbcComponentSwizzle& swizzle,
|
const DxbcComponentSwizzle& swizzle,
|
||||||
DxbcComponentMask mask);
|
DxbcComponentMask mask);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Writes to parts of a vector register
|
|
||||||
*
|
|
||||||
* Note that the source value must have the same
|
|
||||||
* number of components as the write mask.
|
|
||||||
* \param [in] dst Destination value ID
|
|
||||||
* \param [in] src Source value ID
|
|
||||||
* \param [in] mask Write mask
|
|
||||||
* \returns New destination value ID
|
|
||||||
*/
|
|
||||||
DxbcValue regInsert(
|
DxbcValue regInsert(
|
||||||
const DxbcValue& dst,
|
const DxbcValue& dst,
|
||||||
const DxbcValue& src,
|
const DxbcValue& src,
|
||||||
DxbcComponentMask mask);
|
DxbcComponentMask mask);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Loads register
|
|
||||||
*
|
|
||||||
* \param [in] ptr Register pointer
|
|
||||||
* \returns The register value ID
|
|
||||||
*/
|
|
||||||
DxbcValue regLoad(
|
DxbcValue regLoad(
|
||||||
const DxbcPointer& ptr);
|
const DxbcPointer& ptr);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Stores register
|
|
||||||
*
|
|
||||||
* \param [in] ptr Register pointer
|
|
||||||
* \param [in] val Value ID to store
|
|
||||||
* \param [in] mask Write mask
|
|
||||||
*/
|
|
||||||
void regStore(
|
void regStore(
|
||||||
const DxbcPointer& ptr,
|
const DxbcPointer& ptr,
|
||||||
const DxbcValue& val,
|
const DxbcValue& val,
|
||||||
DxbcComponentMask mask);
|
DxbcComponentMask mask);
|
||||||
|
|
||||||
/**
|
virtual void dclInterfaceVar(
|
||||||
* \brief Finalizes shader
|
DxbcOperandType regType,
|
||||||
*
|
uint32_t regId,
|
||||||
* Depending on the shader stage, this may generate
|
uint32_t regDim,
|
||||||
* additional code to set up input variables, output
|
DxbcComponentMask regMask,
|
||||||
* variables, and execute shader phases.
|
DxbcSystemValue sv) = 0;
|
||||||
* \returns DXVK shader module
|
|
||||||
*/
|
virtual DxbcPointer ptrInterfaceVar(
|
||||||
|
DxbcOperandType regType,
|
||||||
|
uint32_t regId) = 0;
|
||||||
|
|
||||||
|
virtual DxbcPointer ptrInterfaceVarIndexed(
|
||||||
|
DxbcOperandType regType,
|
||||||
|
uint32_t regId,
|
||||||
|
const DxbcValue& index) = 0;
|
||||||
|
|
||||||
virtual Rc<DxvkShader> finalize() = 0;
|
virtual Rc<DxvkShader> finalize() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Creates code generator for a given program type
|
|
||||||
*
|
|
||||||
* \param [in] version Program version
|
|
||||||
* \returns The code generator
|
|
||||||
*/
|
|
||||||
static Rc<DxbcCodeGen> create(
|
static Rc<DxbcCodeGen> create(
|
||||||
const DxbcProgramVersion& version);
|
const DxbcProgramVersion& version);
|
||||||
|
|
||||||
|
@ -21,38 +21,34 @@ namespace dxvk {
|
|||||||
void DxvkContext::beginRecording(
|
void DxvkContext::beginRecording(
|
||||||
const Rc<DxvkRecorder>& recorder) {
|
const Rc<DxvkRecorder>& recorder) {
|
||||||
TRACE(this, recorder);
|
TRACE(this, recorder);
|
||||||
|
|
||||||
m_cmd = recorder;
|
m_cmd = recorder;
|
||||||
m_cmd->beginRecording();
|
m_cmd->beginRecording();
|
||||||
|
|
||||||
// Make sure that we apply the current context state
|
// The current state of the internal command buffer is
|
||||||
// to the command buffer when recording draw commands.
|
// undefined, so we have to bind and set up everything
|
||||||
m_state.g.flags.clr(
|
// before any draw or dispatch command is recorded.
|
||||||
DxvkGraphicsPipelineBit::RenderPassBound);
|
m_state.flags.clr(
|
||||||
m_state.g.flags.set(
|
DxvkContextFlag::GpRenderPassBound);
|
||||||
DxvkGraphicsPipelineBit::PipelineDirty,
|
|
||||||
DxvkGraphicsPipelineBit::PipelineStateDirty,
|
|
||||||
DxvkGraphicsPipelineBit::DirtyResources,
|
|
||||||
DxvkGraphicsPipelineBit::DirtyVertexBuffers,
|
|
||||||
DxvkGraphicsPipelineBit::DirtyIndexBuffer);
|
|
||||||
|
|
||||||
m_state.c.flags.set(
|
m_state.flags.set(
|
||||||
DxvkComputePipelineBit::PipelineDirty,
|
DxvkContextFlag::GpDirtyPipeline,
|
||||||
DxvkComputePipelineBit::DirtyResources);
|
DxvkContextFlag::GpDirtyPipelineState,
|
||||||
|
DxvkContextFlag::GpDirtyResources,
|
||||||
|
DxvkContextFlag::GpDirtyIndexBuffers,
|
||||||
|
DxvkContextFlag::GpDirtyVertexBuffers,
|
||||||
|
DxvkContextFlag::CpDirtyPipeline,
|
||||||
|
DxvkContextFlag::CpDirtyResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DxvkContext::endRecording() {
|
void DxvkContext::endRecording() {
|
||||||
TRACE(this);
|
TRACE(this);
|
||||||
|
|
||||||
// Any currently active render pass must be
|
this->renderPassEnd();
|
||||||
// ended before finalizing the command buffer.
|
|
||||||
if (m_state.g.flags.test(DxvkGraphicsPipelineBit::RenderPassBound))
|
|
||||||
this->endRenderPass();
|
|
||||||
|
|
||||||
// Finalize the command list
|
|
||||||
m_cmd->endRecording();
|
m_cmd->endRecording();
|
||||||
m_cmd = nullptr;
|
m_cmd = nullptr;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -60,12 +56,9 @@ namespace dxvk {
|
|||||||
const Rc<DxvkFramebuffer>& fb) {
|
const Rc<DxvkFramebuffer>& fb) {
|
||||||
TRACE(this, fb);
|
TRACE(this, fb);
|
||||||
|
|
||||||
if (m_state.g.fb != fb) {
|
if (m_state.om.framebuffer != fb) {
|
||||||
m_state.g.fb = fb;
|
m_state.om.framebuffer = fb;
|
||||||
|
this->renderPassEnd();
|
||||||
if (m_state.g.flags.test(
|
|
||||||
DxvkGraphicsPipelineBit::RenderPassBound))
|
|
||||||
this->endRenderPass();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,63 +68,42 @@ namespace dxvk {
|
|||||||
const Rc<DxvkShader>& shader) {
|
const Rc<DxvkShader>& shader) {
|
||||||
TRACE(this, stage, shader);
|
TRACE(this, stage, shader);
|
||||||
|
|
||||||
DxvkShaderState* state = this->getShaderState(stage);
|
DxvkShaderStageState* stageState = this->getShaderStage(stage);
|
||||||
|
|
||||||
if (state->shader != shader) {
|
if (stageState->shader != shader) {
|
||||||
state->shader = shader;
|
stageState->shader = shader;
|
||||||
this->setPipelineDirty(stage);
|
|
||||||
|
if (stage == VK_SHADER_STAGE_COMPUTE_BIT) {
|
||||||
|
m_state.flags.set(
|
||||||
|
DxvkContextFlag::CpDirtyPipeline,
|
||||||
|
DxvkContextFlag::CpDirtyResources);
|
||||||
|
} else {
|
||||||
|
m_state.flags.set(
|
||||||
|
DxvkContextFlag::GpDirtyPipeline,
|
||||||
|
DxvkContextFlag::GpDirtyPipelineState,
|
||||||
|
DxvkContextFlag::GpDirtyResources,
|
||||||
|
DxvkContextFlag::GpDirtyVertexBuffers,
|
||||||
|
DxvkContextFlag::GpDirtyIndexBuffers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::bindStorageBuffer(
|
|
||||||
VkShaderStageFlagBits stage,
|
|
||||||
uint32_t slot,
|
|
||||||
const Rc<DxvkBuffer>& buffer,
|
|
||||||
VkDeviceSize offset,
|
|
||||||
VkDeviceSize length) {
|
|
||||||
TRACE(this, stage, slot);
|
|
||||||
|
|
||||||
DxvkBufferBinding binding(buffer, offset, length);
|
|
||||||
DxvkShaderState* state = this->getShaderState(stage);
|
|
||||||
|
|
||||||
// TODO investigate whether it is worth checking whether
|
|
||||||
// the shader actually uses the resource. However, if the
|
|
||||||
// application is not completely retarded, always setting
|
|
||||||
// the 'resources dirty' flag should be the best option.
|
|
||||||
if (state->boundStorageBuffers.at(slot) != binding) {
|
|
||||||
state->boundStorageBuffers.at(slot) = binding;
|
|
||||||
this->setResourcesDirty(stage);
|
|
||||||
m_cmd->trackResource(binding.resource());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::clearRenderTarget(
|
void DxvkContext::clearRenderTarget(
|
||||||
const VkClearAttachment& attachment,
|
const VkClearAttachment& attachment,
|
||||||
const VkClearRect& clearArea) {
|
const VkClearRect& clearArea) {
|
||||||
this->flushGraphicsState();
|
TRACE(this);
|
||||||
|
|
||||||
|
// We only need the framebuffer to be bound. Flushing the
|
||||||
|
// entire pipeline state is not required and might actually
|
||||||
|
// cause problems if the current pipeline state is invalid.
|
||||||
|
this->renderPassBegin();
|
||||||
|
|
||||||
m_cmd->cmdClearAttachments(
|
m_cmd->cmdClearAttachments(
|
||||||
1, &attachment, 1, &clearArea);
|
1, &attachment, 1, &clearArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::dispatch(
|
|
||||||
uint32_t wgCountX,
|
|
||||||
uint32_t wgCountY,
|
|
||||||
uint32_t wgCountZ) {
|
|
||||||
TRACE(this, wgCountX, wgCountY, wgCountZ);
|
|
||||||
this->endRenderPass();
|
|
||||||
this->flushComputeState();
|
|
||||||
|
|
||||||
m_cmd->cmdDispatch(
|
|
||||||
wgCountX, wgCountY, wgCountZ);
|
|
||||||
|
|
||||||
// TODO resource barriers
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::draw(
|
void DxvkContext::draw(
|
||||||
uint32_t vertexCount,
|
uint32_t vertexCount,
|
||||||
uint32_t instanceCount,
|
uint32_t instanceCount,
|
||||||
@ -139,11 +111,6 @@ namespace dxvk {
|
|||||||
uint32_t firstInstance) {
|
uint32_t firstInstance) {
|
||||||
TRACE(this, vertexCount, instanceCount,
|
TRACE(this, vertexCount, instanceCount,
|
||||||
firstVertex, firstInstance);
|
firstVertex, firstInstance);
|
||||||
this->flushGraphicsState();
|
|
||||||
|
|
||||||
m_cmd->cmdDraw(
|
|
||||||
vertexCount, instanceCount,
|
|
||||||
firstVertex, firstInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -155,162 +122,86 @@ namespace dxvk {
|
|||||||
uint32_t firstInstance) {
|
uint32_t firstInstance) {
|
||||||
TRACE(this, indexCount, instanceCount,
|
TRACE(this, indexCount, instanceCount,
|
||||||
firstIndex, vertexOffset, firstInstance);
|
firstIndex, vertexOffset, firstInstance);
|
||||||
this->flushGraphicsState();
|
|
||||||
|
|
||||||
m_cmd->cmdDrawIndexed(
|
|
||||||
indexCount, instanceCount,
|
|
||||||
firstIndex, vertexOffset,
|
|
||||||
firstInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::flushComputeState() {
|
void DxvkContext::renderPassBegin() {
|
||||||
if (m_state.c.flags.test(DxvkComputePipelineBit::PipelineDirty)) {
|
if (!m_state.flags.test(DxvkContextFlag::GpRenderPassBound)
|
||||||
m_state.c.pipeline = m_pipeMgr->getComputePipeline(m_state.c.cs.shader);
|
&& (m_state.om.framebuffer != nullptr)) {
|
||||||
|
m_state.flags.set(DxvkContextFlag::GpRenderPassBound);
|
||||||
|
|
||||||
if (m_state.c.pipeline != nullptr) {
|
const DxvkFramebufferSize fbSize
|
||||||
m_cmd->cmdBindPipeline(
|
= m_state.om.framebuffer->size();
|
||||||
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
||||||
m_state.c.pipeline->getPipelineHandle());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_state.c.flags.test(DxvkComputePipelineBit::DirtyResources)
|
VkRect2D renderArea;
|
||||||
&& m_state.c.pipeline != nullptr) {
|
renderArea.offset = VkOffset2D { 0, 0 };
|
||||||
std::vector<DxvkResourceBinding> bindings;
|
renderArea.extent = VkExtent2D { fbSize.width, fbSize.height };
|
||||||
this->addResourceBindingInfo(bindings, m_state.c.cs);
|
|
||||||
|
|
||||||
m_cmd->bindShaderResources(
|
|
||||||
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
||||||
m_state.c.pipeline->pipelineLayout(),
|
|
||||||
m_state.c.pipeline->descriptorSetLayout(),
|
|
||||||
bindings.size(), bindings.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state.c.flags.clr(
|
|
||||||
DxvkComputePipelineBit::PipelineDirty,
|
|
||||||
DxvkComputePipelineBit::DirtyResources);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::flushGraphicsState() {
|
|
||||||
if (!m_state.g.flags.test(DxvkGraphicsPipelineBit::RenderPassBound))
|
|
||||||
this->beginRenderPass();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::beginRenderPass() {
|
|
||||||
TRACE(this);
|
|
||||||
|
|
||||||
DxvkFramebufferSize fbsize
|
|
||||||
= m_state.g.fb->size();
|
|
||||||
|
|
||||||
VkRenderPassBeginInfo info;
|
VkRenderPassBeginInfo info;
|
||||||
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||||
info.pNext = nullptr;
|
info.pNext = nullptr;
|
||||||
info.renderPass = m_state.g.fb->renderPass();
|
info.renderPass = m_state.om.framebuffer->renderPass();
|
||||||
info.framebuffer = m_state.g.fb->handle();
|
info.framebuffer = m_state.om.framebuffer->handle();
|
||||||
info.renderArea = VkRect2D { { 0, 0 }, { fbsize.width, fbsize.height } };
|
info.renderArea = renderArea;
|
||||||
info.clearValueCount = 0;
|
info.clearValueCount = 0;
|
||||||
info.pClearValues = nullptr;
|
info.pClearValues = nullptr;
|
||||||
|
|
||||||
m_cmd->cmdBeginRenderPass(&info, VK_SUBPASS_CONTENTS_INLINE);
|
m_cmd->cmdBeginRenderPass(&info,
|
||||||
m_state.g.flags.set(DxvkGraphicsPipelineBit::RenderPassBound);
|
VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::endRenderPass() {
|
void DxvkContext::renderPassEnd() {
|
||||||
TRACE(this);
|
if (m_state.flags.test(DxvkContextFlag::GpRenderPassBound)) {
|
||||||
|
m_state.flags.clr(DxvkContextFlag::GpRenderPassBound);
|
||||||
m_cmd->cmdEndRenderPass();
|
m_cmd->cmdEndRenderPass();
|
||||||
m_state.g.flags.clr(DxvkGraphicsPipelineBit::RenderPassBound);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::setPipelineDirty(VkShaderStageFlagBits stage) {
|
|
||||||
if (stage == VK_SHADER_STAGE_COMPUTE_BIT) {
|
|
||||||
m_state.c.flags.set(
|
|
||||||
DxvkComputePipelineBit::PipelineDirty,
|
|
||||||
DxvkComputePipelineBit::DirtyResources);
|
|
||||||
} else {
|
|
||||||
m_state.g.flags.set(
|
|
||||||
DxvkGraphicsPipelineBit::PipelineDirty,
|
|
||||||
DxvkGraphicsPipelineBit::DirtyResources);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::setResourcesDirty(VkShaderStageFlagBits stage) {
|
void DxvkContext::bindGraphicsPipeline() {
|
||||||
if (stage == VK_SHADER_STAGE_COMPUTE_BIT)
|
if (m_state.flags.test(DxvkContextFlag::GpDirtyPipeline)) {
|
||||||
m_state.c.flags.set(DxvkComputePipelineBit::DirtyResources);
|
m_state.flags.clr(DxvkContextFlag::GpDirtyPipeline);
|
||||||
else
|
|
||||||
m_state.g.flags.set(DxvkGraphicsPipelineBit::DirtyResources);
|
m_state.activeGraphicsPipeline = m_pipeMgr->getGraphicsPipeline(
|
||||||
|
m_state.vs.shader, m_state.tcs.shader, m_state.tes.shader,
|
||||||
|
m_state.gs.shader, m_state.fs.shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state.flags.test(DxvkContextFlag::GpDirtyPipelineState)
|
||||||
|
&& m_state.activeGraphicsPipeline != nullptr) {
|
||||||
|
m_state.flags.clr(DxvkContextFlag::GpDirtyPipelineState);
|
||||||
|
|
||||||
|
DxvkGraphicsPipelineStateInfo gpState;
|
||||||
|
gpState.renderPass = m_state.om.framebuffer->renderPass();
|
||||||
|
|
||||||
|
m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
|
m_state.activeGraphicsPipeline->getPipelineHandle(gpState));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DxvkShaderState* DxvkContext::getShaderState(VkShaderStageFlagBits stage) {
|
void DxvkContext::flushGraphicsState() {
|
||||||
|
this->renderPassBegin();
|
||||||
|
this->bindGraphicsPipeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxvkShaderStageState* DxvkContext::getShaderStage(VkShaderStageFlagBits stage) {
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case VK_SHADER_STAGE_VERTEX_BIT:
|
case VK_SHADER_STAGE_VERTEX_BIT: return &m_state.vs;
|
||||||
return &m_state.g.vs;
|
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return &m_state.tcs;
|
||||||
|
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return &m_state.tes;
|
||||||
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
|
case VK_SHADER_STAGE_GEOMETRY_BIT: return &m_state.gs;
|
||||||
return &m_state.g.tcs;
|
case VK_SHADER_STAGE_FRAGMENT_BIT: return &m_state.fs;
|
||||||
|
case VK_SHADER_STAGE_COMPUTE_BIT: return &m_state.cs;
|
||||||
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
|
|
||||||
return &m_state.g.tes;
|
|
||||||
|
|
||||||
case VK_SHADER_STAGE_GEOMETRY_BIT:
|
|
||||||
return &m_state.g.gs;
|
|
||||||
|
|
||||||
case VK_SHADER_STAGE_FRAGMENT_BIT:
|
|
||||||
return &m_state.g.fs;
|
|
||||||
|
|
||||||
case VK_SHADER_STAGE_COMPUTE_BIT:
|
|
||||||
return &m_state.c.cs;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
throw DxvkError(str::format(
|
||||||
|
"DxvkContext::getShaderStage: Invalid stage bit: ",
|
||||||
|
static_cast<uint32_t>(stage)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t DxvkContext::addResourceBindingInfo(
|
|
||||||
std::vector<DxvkResourceBinding>& bindings,
|
|
||||||
const DxvkShaderState& stageInfo) const {
|
|
||||||
const uint32_t slotCount = stageInfo.shader->slotCount();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < slotCount; i++) {
|
|
||||||
DxvkResourceSlot slot = stageInfo.shader->slot(i);
|
|
||||||
DxvkResourceBinding binding;
|
|
||||||
|
|
||||||
switch (slot.type) {
|
|
||||||
case DxvkResourceType::ImageSampler:
|
|
||||||
binding.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DxvkResourceType::SampledImage:
|
|
||||||
binding.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DxvkResourceType::StorageImage:
|
|
||||||
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DxvkResourceType::UniformBuffer:
|
|
||||||
binding.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
||||||
binding.buffer = stageInfo.boundUniformBuffers.at(slot.slot).descriptorInfo();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DxvkResourceType::StorageBuffer:
|
|
||||||
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
||||||
binding.buffer = stageInfo.boundStorageBuffers.at(slot.slot).descriptorInfo();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings.push_back(binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
return slotCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -43,17 +43,10 @@ namespace dxvk {
|
|||||||
* The command list can then be submitted to
|
* The command list can then be submitted to
|
||||||
* the device.
|
* the device.
|
||||||
*
|
*
|
||||||
* The return value of this method can be used to
|
|
||||||
* determine whether the command list needs to be
|
|
||||||
* submitted. In case the command list is empty,
|
|
||||||
* \c false will be returned and it shall not be
|
|
||||||
* submitted to the device.
|
|
||||||
*
|
|
||||||
* This will not change any context state
|
* This will not change any context state
|
||||||
* other than the active command list.
|
* other than the active command list.
|
||||||
* \returns \c true if any commands were recorded
|
|
||||||
*/
|
*/
|
||||||
bool endRecording();
|
void endRecording();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Sets framebuffer
|
* \brief Sets framebuffer
|
||||||
@ -76,20 +69,6 @@ namespace dxvk {
|
|||||||
VkShaderStageFlagBits stage,
|
VkShaderStageFlagBits stage,
|
||||||
const Rc<DxvkShader>& shader);
|
const Rc<DxvkShader>& shader);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Binds a storage buffer
|
|
||||||
*
|
|
||||||
* \param [in] stage Shader stage for this binding
|
|
||||||
* \param [in] slot Binding slot index
|
|
||||||
* \param [in] buffer Buffer binding info
|
|
||||||
*/
|
|
||||||
void bindStorageBuffer(
|
|
||||||
VkShaderStageFlagBits stage,
|
|
||||||
uint32_t slot,
|
|
||||||
const Rc<DxvkBuffer>& buffer,
|
|
||||||
VkDeviceSize offset,
|
|
||||||
VkDeviceSize length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Clears an active render target
|
* \brief Clears an active render target
|
||||||
*
|
*
|
||||||
@ -100,18 +79,6 @@ namespace dxvk {
|
|||||||
const VkClearAttachment& attachment,
|
const VkClearAttachment& attachment,
|
||||||
const VkClearRect& clearArea);
|
const VkClearRect& clearArea);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Dispatches compute operations
|
|
||||||
*
|
|
||||||
* \param [in] wgCountX Number of X work groups
|
|
||||||
* \param [in] wgCountY Number of Y work groups
|
|
||||||
* \param [in] wgCountZ Number of Z work groups
|
|
||||||
*/
|
|
||||||
void dispatch(
|
|
||||||
uint32_t wgCountX,
|
|
||||||
uint32_t wgCountY,
|
|
||||||
uint32_t wgCountZ);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Draws primitive without using an index buffer
|
* \brief Draws primitive without using an index buffer
|
||||||
*
|
*
|
||||||
@ -150,26 +117,17 @@ namespace dxvk {
|
|||||||
Rc<DxvkRecorder> m_cmd;
|
Rc<DxvkRecorder> m_cmd;
|
||||||
DxvkContextState m_state;
|
DxvkContextState m_state;
|
||||||
|
|
||||||
void flushComputeState();
|
void renderPassBegin();
|
||||||
|
|
||||||
|
void renderPassEnd();
|
||||||
|
|
||||||
|
void bindGraphicsPipeline();
|
||||||
|
|
||||||
void flushGraphicsState();
|
void flushGraphicsState();
|
||||||
|
|
||||||
void beginRenderPass();
|
DxvkShaderStageState* getShaderStage(
|
||||||
void endRenderPass();
|
|
||||||
|
|
||||||
void setPipelineDirty(VkShaderStageFlagBits stage);
|
|
||||||
void setResourcesDirty(VkShaderStageFlagBits stage);
|
|
||||||
|
|
||||||
void shaderResourceBarriers(
|
|
||||||
DxvkBarrierSet& barriers,
|
|
||||||
VkShaderStageFlagBits stage);
|
VkShaderStageFlagBits stage);
|
||||||
|
|
||||||
DxvkShaderState* getShaderState(
|
|
||||||
VkShaderStageFlagBits stage);
|
|
||||||
|
|
||||||
uint32_t addResourceBindingInfo(
|
|
||||||
std::vector<DxvkResourceBinding>& bindings,
|
|
||||||
const DxvkShaderState& stageInfo) const;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -3,6 +3,7 @@
|
|||||||
#include "dxvk_buffer.h"
|
#include "dxvk_buffer.h"
|
||||||
#include "dxvk_compute.h"
|
#include "dxvk_compute.h"
|
||||||
#include "dxvk_framebuffer.h"
|
#include "dxvk_framebuffer.h"
|
||||||
|
#include "dxvk_graphics.h"
|
||||||
#include "dxvk_image.h"
|
#include "dxvk_image.h"
|
||||||
#include "dxvk_limits.h"
|
#include "dxvk_limits.h"
|
||||||
#include "dxvk_shader.h"
|
#include "dxvk_shader.h"
|
||||||
@ -16,87 +17,64 @@ namespace dxvk {
|
|||||||
* graphics pipeline has changed and/or needs to
|
* graphics pipeline has changed and/or needs to
|
||||||
* be updated.
|
* be updated.
|
||||||
*/
|
*/
|
||||||
enum class DxvkGraphicsPipelineBit : uint64_t {
|
enum class DxvkContextFlag : uint64_t {
|
||||||
RenderPassBound = 0, ///< If set, a render pass instance is currently active
|
GpRenderPassBound, ///< Render pass is currently bound
|
||||||
PipelineDirty = 1, ///< If set, the shader pipeline binding is out of date
|
GpDirtyPipeline, ///< Graphics pipeline binding are out of date
|
||||||
PipelineStateDirty = 2, ///< If set, another pipeline variant needs to be bound
|
GpDirtyPipelineState, ///< Graphics pipeline state (blending etc.) is dirty
|
||||||
DirtyResources = 3, ///< If set, the descriptor set must be updated
|
GpDirtyResources, ///< Graphics pipeline resource bindings are out of date
|
||||||
DirtyVertexBuffers = 4, ///< If set, the vertex buffer bindings need to be updated
|
GpDirtyVertexBuffers, ///< Vertex buffer bindings are out of date
|
||||||
DirtyIndexBuffer = 5, ///< If set, the index buffer binding needs to be updated
|
GpDirtyIndexBuffers, ///< Index buffer binding are out of date
|
||||||
|
|
||||||
|
CpDirtyPipeline, ///< Compute pipeline binding are out of date
|
||||||
|
CpDirtyResources, ///< Compute pipeline resource bindings are out of date
|
||||||
};
|
};
|
||||||
|
|
||||||
using DxvkGraphicsPipelineFlags = Flags<DxvkGraphicsPipelineBit>;
|
using DxvkContextFlags = Flags<DxvkContextFlag>;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Compute pipeline state flags
|
|
||||||
*
|
|
||||||
* Stores information on whether the compute shader
|
|
||||||
* or any of its resource bindings have been updated.
|
|
||||||
*/
|
|
||||||
enum class DxvkComputePipelineBit : uint64_t {
|
|
||||||
PipelineDirty = 0, ///< If set, the shader pipeline binding is out of date
|
|
||||||
DirtyResources = 1, ///< If set, the descriptor set must be updated
|
|
||||||
};
|
|
||||||
|
|
||||||
using DxvkComputePipelineFlags = Flags<DxvkComputePipelineBit>;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Shader state
|
* \brief Shader state
|
||||||
*
|
*
|
||||||
* Stores the active shader and resources for a single
|
* Stores the active shader and resources
|
||||||
* shader stage. This includes sampled textures, uniform
|
* for a single shader stage. All stages
|
||||||
* buffers, storage buffers and storage images.
|
* support the same types of resources.
|
||||||
*/
|
*/
|
||||||
struct DxvkShaderState {
|
struct DxvkShaderStageState {
|
||||||
Rc<DxvkShader> shader;
|
Rc<DxvkShader> shader;
|
||||||
|
|
||||||
std::array<DxvkBufferBinding, MaxNumStorageBuffers> boundStorageBuffers;
|
|
||||||
std::array<DxvkBufferBinding, MaxNumUniformBuffers> boundUniformBuffers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Graphics pipeline state
|
* \brief Output merger state
|
||||||
*
|
*
|
||||||
* Stores everything related to graphics
|
* Stores the active framebuffer and the current
|
||||||
* operations, including bound resources.
|
* blend state, as well as the depth stencil state.
|
||||||
*/
|
*/
|
||||||
struct DxvkGraphicsPipelineState {
|
struct DxvkOutputMergerState {
|
||||||
DxvkShaderState vs;
|
Rc<DxvkFramebuffer> framebuffer;
|
||||||
DxvkShaderState tcs;
|
|
||||||
DxvkShaderState tes;
|
|
||||||
DxvkShaderState gs;
|
|
||||||
DxvkShaderState fs;
|
|
||||||
Rc<DxvkFramebuffer> fb;
|
|
||||||
DxvkGraphicsPipelineFlags flags;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Compute pipeline state
|
* \brief Pipeline state
|
||||||
*
|
*
|
||||||
* Stores the active compute pipeline and
|
* Stores all bound shaders, resources,
|
||||||
* resources bound to the compute shader.
|
* and constant pipeline state objects.
|
||||||
*/
|
|
||||||
struct DxvkComputePipelineState {
|
|
||||||
DxvkShaderState cs;
|
|
||||||
Rc<DxvkComputePipeline> pipeline;
|
|
||||||
DxvkComputePipelineFlags flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief DXVK context state
|
|
||||||
*
|
|
||||||
* Stores all graphics pipeline state known
|
|
||||||
* to DXVK. As in Vulkan, graphics and compute
|
|
||||||
* pipeline states are strictly separated.
|
|
||||||
*/
|
*/
|
||||||
struct DxvkContextState {
|
struct DxvkContextState {
|
||||||
DxvkGraphicsPipelineState g;
|
DxvkShaderStageState vs;
|
||||||
DxvkComputePipelineState c;
|
DxvkShaderStageState tcs;
|
||||||
|
DxvkShaderStageState tes;
|
||||||
|
DxvkShaderStageState gs;
|
||||||
|
DxvkShaderStageState fs;
|
||||||
|
DxvkShaderStageState cs;
|
||||||
|
|
||||||
|
DxvkOutputMergerState om;
|
||||||
|
|
||||||
|
Rc<DxvkGraphicsPipeline> activeGraphicsPipeline;
|
||||||
|
Rc<DxvkComputePipeline> activeComputePipeline;
|
||||||
|
|
||||||
|
DxvkContextFlags flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
bool DxvkGraphicsPipelineStateInfo::operator == (const DxvkGraphicsPipelineStateInfo& other) const {
|
bool DxvkGraphicsPipelineStateInfo::operator == (const DxvkGraphicsPipelineStateInfo& other) const {
|
||||||
// TODO implement
|
return this->renderPass == other.renderPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
namespace dxvk {
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Graphics pipeline state info
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
struct DxvkGraphicsPipelineStateInfo {
|
struct DxvkGraphicsPipelineStateInfo {
|
||||||
VkRenderPass renderPass;
|
VkRenderPass renderPass;
|
||||||
|
|
||||||
@ -18,6 +23,7 @@ namespace dxvk {
|
|||||||
bool operator != (const DxvkGraphicsPipelineStateInfo& other) const;
|
bool operator != (const DxvkGraphicsPipelineStateInfo& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Graphics pipeline
|
* \brief Graphics pipeline
|
||||||
*
|
*
|
||||||
|
@ -28,38 +28,6 @@ public:
|
|||||||
})),
|
})),
|
||||||
m_dxvkContext (m_dxvkDevice->createContext()),
|
m_dxvkContext (m_dxvkDevice->createContext()),
|
||||||
m_dxvkCommandList (m_dxvkDevice->createCommandList()) {
|
m_dxvkCommandList (m_dxvkDevice->createCommandList()) {
|
||||||
|
|
||||||
DxvkBufferCreateInfo bufferInfo;
|
|
||||||
bufferInfo.size = sizeof(m_testData);
|
|
||||||
bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
||||||
bufferInfo.stages = VK_PIPELINE_STAGE_HOST_BIT
|
|
||||||
| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
||||||
bufferInfo.access = VK_ACCESS_HOST_WRITE_BIT
|
|
||||||
| VK_ACCESS_HOST_READ_BIT
|
|
||||||
| VK_ACCESS_SHADER_WRITE_BIT
|
|
||||||
| VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
|
|
||||||
m_testBuffer = m_dxvkDevice->createBuffer(bufferInfo,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
||||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
||||||
for (size_t i = 0; i < 64; i++)
|
|
||||||
m_testData[i] = static_cast<int>(i);
|
|
||||||
std::memcpy(m_testBuffer->mapPtr(),
|
|
||||||
m_testData, sizeof(m_testData));
|
|
||||||
|
|
||||||
DxvkResourceSlot computeBufferSlot;
|
|
||||||
computeBufferSlot.mode.set(
|
|
||||||
DxvkResourceModeBit::Read,
|
|
||||||
DxvkResourceModeBit::Write);
|
|
||||||
computeBufferSlot.type = DxvkResourceType::StorageBuffer;
|
|
||||||
computeBufferSlot.slot = 0;
|
|
||||||
|
|
||||||
SpirvCodeBuffer code(std::ifstream("comp.spv", std::ios::binary));
|
|
||||||
code.store(std::ofstream("comp.2.spv", std::ios::binary));
|
|
||||||
|
|
||||||
m_compShader = new DxvkShader(
|
|
||||||
VK_SHADER_STAGE_COMPUTE_BIT, std::move(code),
|
|
||||||
1, &computeBufferSlot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~TriangleApp() {
|
~TriangleApp() {
|
||||||
@ -92,22 +60,12 @@ public:
|
|||||||
m_dxvkContext->clearRenderTarget(
|
m_dxvkContext->clearRenderTarget(
|
||||||
clearAttachment,
|
clearAttachment,
|
||||||
clearArea);
|
clearArea);
|
||||||
m_dxvkContext->bindShader(
|
|
||||||
VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
m_compShader);
|
|
||||||
m_dxvkContext->bindStorageBuffer(
|
|
||||||
VK_SHADER_STAGE_COMPUTE_BIT, 0,
|
|
||||||
m_testBuffer, 0, sizeof(m_testData));
|
|
||||||
m_dxvkContext->dispatch(1, 1, 1);
|
|
||||||
m_dxvkContext->endRecording();
|
m_dxvkContext->endRecording();
|
||||||
|
|
||||||
auto fence = m_dxvkDevice->submitCommandList(
|
auto fence = m_dxvkDevice->submitCommandList(
|
||||||
m_dxvkCommandList, sync1, sync2);
|
m_dxvkCommandList, sync1, sync2);
|
||||||
m_dxvkSwapchain->present(sync2);
|
m_dxvkSwapchain->present(sync2);
|
||||||
m_dxvkDevice->waitForIdle();
|
m_dxvkDevice->waitForIdle();
|
||||||
|
|
||||||
std::memcpy(m_testData, m_testBuffer->mapPtr(), sizeof(m_testData));
|
|
||||||
std::cout << m_testData[0] << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -120,13 +78,6 @@ private:
|
|||||||
Rc<DxvkContext> m_dxvkContext;
|
Rc<DxvkContext> m_dxvkContext;
|
||||||
Rc<DxvkCommandList> m_dxvkCommandList;
|
Rc<DxvkCommandList> m_dxvkCommandList;
|
||||||
|
|
||||||
Rc<DxvkBuffer> m_testBuffer;
|
|
||||||
Rc<DxvkShader> m_compShader;
|
|
||||||
Rc<DxvkShader> m_vertShader;
|
|
||||||
Rc<DxvkShader> m_fragShader;
|
|
||||||
|
|
||||||
int m_testData[64];
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LRESULT CALLBACK WindowProc(HWND hWnd,
|
LRESULT CALLBACK WindowProc(HWND hWnd,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user