rhi: d3d11: Enable tessellation and geometry with some caveats

The caveat being having to manually create HLSL versions of the hull,
domain, and geometry shaders in parallel with the Vulkan GLSL ones,
while keeping the interfaces intact (stage inputs and outputs, cbuffer
layouts, binding points/registers). This is not always trivial but
typically doable in not very complicated case after inspecting the
SPIRV-Cross-generated vertex/fragment code in the .qsb files. Once
written, the HLSL files can be injected into a .qsb file with qsb -r.
or the corresponding CMake syntax. Conceptually this is no different
from how samplerExternalOES support is implemented for Multimedia.
(there the problem is that the shaders cannot be compiled to SPIR-V
to begin with, here it is that we cannot translate from SPIR-V, but
in the end the workaround for both problems is effectively the same)

The manual tests demonstrate this, both the tessellation and geometry
apps work now with D3D out of the box.

On the bright side, the implementation here in the the D3D backend of
QRhi does not need to know about how the shaders got there in the
QShader. So none of the implementation is dependent on this manual
process. If some day qsb would start translating to these kind of
shaders as well, it would all still work as-is.

Change-Id: I32d9ab94e00174e4bd5b59ac814dfedef9f93ad1
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
bb10
Laszlo Agocs 2022-08-12 14:23:44 +02:00
parent 6c9f4f5ebc
commit 5eacc974c7
15 changed files with 489 additions and 299 deletions

View File

@ -677,23 +677,27 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
can be set via
\l{QRhiGraphicsPipeline::setPatchControlPointCount()}{setPatchControlPointCount()},
and shaders for tessellation control and evaluation can be specified in the
QRhiShaderStage list. \b{Tessellation is considered an experimental feature
in QRhi and can only be expected to be supported with Vulkan, OpenGL and
OpenGL ES for the time being}, assuming the implementation reports it as
supported at run time. Tessellation shaders have portability issues between
APIs (for example, translating GLSL/SPIR-V to HLSL is problematic due to
the way hull shaders are structured, whereas Metal uses a somewhat
different tessellation pipeline than others), and therefore no guarantees
can be given for a universal solution for now.
QRhiShaderStage list. Tessellation is considered an experimental feature in
QRhi and can only be expected to be supported with Vulkan, OpenGL (ES), and
Direct 3D, assuming the implementation reports it as supported at run time.
Tessellation shaders have portability issues between APIs (for example,
translating GLSL/SPIR-V to HLSL is problematic due to the way hull shaders
are structured, whereas Metal uses a somewhat different tessellation
pipeline than others), and therefore no guarantees can be given for a
universal solution for now. (for Direct 3D in particular, handwritten HLSL
hull and domain shaders must be injected into each QShader since qsb cannot
generate these from SPIR-V)
\value GeometryShader Indicates that the geometry shader stage is
supported. When supported, a geometry shader can be specified in the
QRhiShaderStage list. \b{Geometry Shaders are considered an experimental
QRhiShaderStage list. Geometry Shaders are considered an experimental
feature in QRhi and can only be expected to be supported with Vulkan,
OpenGL (3.2+) and OpenGL ES (3.2+) for the time being}, assuming the
implementation reports it as supported at run time. Geometry shaders have
portability issues between APIs, and therefore no guarantees can be given
for a universal solution for now.
Direct 3D, OpenGL (3.2+) and OpenGL ES (3.2+), assuming the implementation
reports it as supported at run time. Geometry shaders have portability
issues between APIs, and therefore no guarantees can be given for a
universal solution for now. (for Direct 3D in particular, a handwritten
HLSL geometry shader must be injected into each QShader since qsb cannot
generate this from SPIR-V)
\value TextureArrayRange Indicates that for
\l{QRhi::newTextureArray()}{texture arrays} it is possible to specify a

View File

@ -537,9 +537,9 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
case QRhi::TextureArrays:
return true;
case QRhi::Tessellation:
return false;
return true;
case QRhi::GeometryShader:
return false;
return true;
case QRhi::TextureArrayRange:
return true;
case QRhi::NonFillPolygonMode:
@ -840,10 +840,13 @@ void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
}
}
static const int RBM_SUPPORTED_STAGES = 3;
static const int RBM_SUPPORTED_STAGES = 6;
static const int RBM_VERTEX = 0;
static const int RBM_FRAGMENT = 1;
static const int RBM_COMPUTE = 2;
static const int RBM_HULL = 1;
static const int RBM_DOMAIN = 2;
static const int RBM_GEOMETRY = 3;
static const int RBM_FRAGMENT = 4;
static const int RBM_COMPUTE = 5;
void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
@ -952,6 +955,9 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
memset(resBindMaps, 0, sizeof(resBindMaps));
if (gfxPsD) {
resBindMaps[RBM_VERTEX] = &gfxPsD->vs.nativeResourceBindingMap;
resBindMaps[RBM_HULL] = &gfxPsD->hs.nativeResourceBindingMap;
resBindMaps[RBM_DOMAIN] = &gfxPsD->ds.nativeResourceBindingMap;
resBindMaps[RBM_GEOMETRY] = &gfxPsD->gs.nativeResourceBindingMap;
resBindMaps[RBM_FRAGMENT] = &gfxPsD->fs.nativeResourceBindingMap;
} else {
resBindMaps[RBM_COMPUTE] = &compPsD->cs.nativeResourceBindingMap;
@ -2077,7 +2083,7 @@ static inline QPair<int, int> mapBinding(int binding,
{
const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
if (!map || map->isEmpty())
return { binding, binding }; // old QShader versions do not have this map, assume 1:1 mapping then
return { binding, binding }; // assume 1:1 mapping
auto it = map->constFind(binding);
if (it != map->cend())
@ -2092,31 +2098,21 @@ static inline QPair<int, int> mapBinding(int binding,
void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
{
srbD->vsubufs.clear();
srbD->vsubuforigbindings.clear();
srbD->vsubufoffsets.clear();
srbD->vsubufsizes.clear();
srbD->vsUniformBufferBatches.clear();
srbD->hsUniformBufferBatches.clear();
srbD->dsUniformBufferBatches.clear();
srbD->gsUniformBufferBatches.clear();
srbD->fsUniformBufferBatches.clear();
srbD->csUniformBufferBatches.clear();
srbD->fsubufs.clear();
srbD->fsubuforigbindings.clear();
srbD->fsubufoffsets.clear();
srbD->fsubufsizes.clear();
srbD->vsSamplerBatches.clear();
srbD->hsSamplerBatches.clear();
srbD->dsSamplerBatches.clear();
srbD->gsSamplerBatches.clear();
srbD->fsSamplerBatches.clear();
srbD->csSamplerBatches.clear();
srbD->csubufs.clear();
srbD->csubuforigbindings.clear();
srbD->csubufoffsets.clear();
srbD->csubufsizes.clear();
srbD->vssamplers.clear();
srbD->vsshaderresources.clear();
srbD->fssamplers.clear();
srbD->fsshaderresources.clear();
srbD->cssamplers.clear();
srbD->csshaderresources.clear();
srbD->csUAVs.clear();
srbD->csUavBatches.clear();
struct Stage {
struct Buffer {
@ -2142,6 +2138,30 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
QVarLengthArray<Texture, 8> textures;
QVarLengthArray<Sampler, 8> samplers;
QVarLengthArray<Uav, 8> uavs;
void buildBufferBatches(QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches) const
{
for (const Buffer &buf : buffers) {
batches.ubufs.feed(buf.breg, buf.buffer);
batches.ubuforigbindings.feed(buf.breg, UINT(buf.binding));
batches.ubufoffsets.feed(buf.breg, buf.offsetInConstants);
batches.ubufsizes.feed(buf.breg, buf.sizeInConstants);
}
batches.finish();
}
void buildSamplerBatches(QD3D11ShaderResourceBindings::StageSamplerBatches &batches) const
{
for (const Texture &t : textures)
batches.shaderresources.feed(t.treg, t.srv);
for (const Sampler &s : samplers)
batches.samplers.feed(s.sreg, s.sampler);
batches.finish();
}
void buildUavBatches(QD3D11ShaderResourceBindings::StageUavBatches &batches) const
{
for (const Stage::Uav &u : uavs)
batches.uavs.feed(u.ureg, u.uav);
batches.finish();
}
} res[RBM_SUPPORTED_STAGES];
for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
@ -2170,6 +2190,21 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
if (nativeBinding.first >= 0)
res[RBM_VERTEX].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
if (nativeBinding.first >= 0)
res[RBM_HULL].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
if (nativeBinding.first >= 0)
res[RBM_DOMAIN].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
if (nativeBinding.first >= 0)
res[RBM_GEOMETRY].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
if (nativeBinding.first >= 0)
@ -2189,6 +2224,9 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
bd.stex.count = data->count;
const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingHull = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingDomain = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingGeom = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
// if SPIR-V binding b is mapped to tN and sN in HLSL, and it
@ -2201,11 +2239,11 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
bd.stex.d[elem].texGeneration = texD ? texD->generation : 0;
bd.stex.d[elem].samplerId = samplerD ? samplerD->m_id : 0;
bd.stex.d[elem].samplerGeneration = samplerD ? samplerD->generation : 0;
// Must handle all three cases (combined, separate, separate):
// first = texture binding, second = sampler binding
// first = texture binding
// first = sampler binding
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
// Must handle all three cases (combined, separate, separate):
// first = texture binding, second = sampler binding
// first = texture binding
// first = sampler binding
const int samplerBinding = texD && samplerD ? nativeBindingVert.second
: (samplerD ? nativeBindingVert.first : -1);
if (nativeBindingVert.first >= 0 && texD)
@ -2213,9 +2251,33 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
if (samplerBinding >= 0)
res[RBM_VERTEX].samplers.append({ samplerBinding + elem, samplerD->samplerState });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingHull.second
: (samplerD ? nativeBindingHull.first : -1);
if (nativeBindingHull.first >= 0 && texD)
res[RBM_HULL].textures.append({ nativeBindingHull.first + elem, texD->srv });
if (samplerBinding >= 0)
res[RBM_HULL].samplers.append({ samplerBinding + elem, samplerD->samplerState });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingDomain.second
: (samplerD ? nativeBindingDomain.first : -1);
if (nativeBindingDomain.first >= 0 && texD)
res[RBM_DOMAIN].textures.append({ nativeBindingDomain.first + elem, texD->srv });
if (samplerBinding >= 0)
res[RBM_DOMAIN].samplers.append({ samplerBinding + elem, samplerD->samplerState });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingGeom.second
: (samplerD ? nativeBindingGeom.first : -1);
if (nativeBindingGeom.first >= 0 && texD)
res[RBM_GEOMETRY].textures.append({ nativeBindingGeom.first + elem, texD->srv });
if (samplerBinding >= 0)
res[RBM_GEOMETRY].samplers.append({ samplerBinding + elem, samplerD->samplerState });
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingFrag.second
: (samplerD ? nativeBindingVert.first : -1);
: (samplerD ? nativeBindingFrag.first : -1);
if (nativeBindingFrag.first >= 0 && texD)
res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
if (samplerBinding >= 0)
@ -2223,7 +2285,7 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingComp.second
: (samplerD ? nativeBindingVert.first : -1);
: (samplerD ? nativeBindingComp.first : -1);
if (nativeBindingComp.first >= 0 && texD)
res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
if (samplerBinding >= 0)
@ -2295,63 +2357,21 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
});
}
for (const Stage::Buffer &buf : qAsConst(res[RBM_VERTEX].buffers)) {
srbD->vsubufs.feed(buf.breg, buf.buffer);
srbD->vsubuforigbindings.feed(buf.breg, UINT(buf.binding));
srbD->vsubufoffsets.feed(buf.breg, buf.offsetInConstants);
srbD->vsubufsizes.feed(buf.breg, buf.sizeInConstants);
}
srbD->vsubufsPresent = srbD->vsubufs.finish();
srbD->vsubuforigbindings.finish();
srbD->vsubufoffsets.finish();
srbD->vsubufsizes.finish();
res[RBM_VERTEX].buildBufferBatches(srbD->vsUniformBufferBatches);
res[RBM_HULL].buildBufferBatches(srbD->hsUniformBufferBatches);
res[RBM_DOMAIN].buildBufferBatches(srbD->dsUniformBufferBatches);
res[RBM_GEOMETRY].buildBufferBatches(srbD->gsUniformBufferBatches);
res[RBM_FRAGMENT].buildBufferBatches(srbD->fsUniformBufferBatches);
res[RBM_COMPUTE].buildBufferBatches(srbD->csUniformBufferBatches);
for (const Stage::Buffer &buf : qAsConst(res[RBM_FRAGMENT].buffers)) {
srbD->fsubufs.feed(buf.breg, buf.buffer);
srbD->fsubuforigbindings.feed(buf.breg, UINT(buf.binding));
srbD->fsubufoffsets.feed(buf.breg, buf.offsetInConstants);
srbD->fsubufsizes.feed(buf.breg, buf.sizeInConstants);
}
srbD->fsubufsPresent = srbD->fsubufs.finish();
srbD->fsubuforigbindings.finish();
srbD->fsubufoffsets.finish();
srbD->fsubufsizes.finish();
res[RBM_VERTEX].buildSamplerBatches(srbD->vsSamplerBatches);
res[RBM_HULL].buildSamplerBatches(srbD->hsSamplerBatches);
res[RBM_DOMAIN].buildSamplerBatches(srbD->dsSamplerBatches);
res[RBM_GEOMETRY].buildSamplerBatches(srbD->gsSamplerBatches);
res[RBM_FRAGMENT].buildSamplerBatches(srbD->fsSamplerBatches);
res[RBM_COMPUTE].buildSamplerBatches(srbD->csSamplerBatches);
for (const Stage::Buffer &buf : qAsConst(res[RBM_COMPUTE].buffers)) {
srbD->csubufs.feed(buf.breg, buf.buffer);
srbD->csubuforigbindings.feed(buf.breg, UINT(buf.binding));
srbD->csubufoffsets.feed(buf.breg, buf.offsetInConstants);
srbD->csubufsizes.feed(buf.breg, buf.sizeInConstants);
}
srbD->csubufsPresent = srbD->csubufs.finish();
srbD->csubuforigbindings.finish();
srbD->csubufoffsets.finish();
srbD->csubufsizes.finish();
for (const Stage::Texture &t : qAsConst(res[RBM_VERTEX].textures))
srbD->vsshaderresources.feed(t.treg, t.srv);
for (const Stage::Sampler &s : qAsConst(res[RBM_VERTEX].samplers))
srbD->vssamplers.feed(s.sreg, s.sampler);
srbD->vssamplersPresent = srbD->vssamplers.finish();
srbD->vsshaderresources.finish();
for (const Stage::Texture &t : qAsConst(res[RBM_FRAGMENT].textures))
srbD->fsshaderresources.feed(t.treg, t.srv);
for (const Stage::Sampler &s : qAsConst(res[RBM_FRAGMENT].samplers))
srbD->fssamplers.feed(s.sreg, s.sampler);
srbD->fssamplersPresent = srbD->fssamplers.finish();
srbD->fsshaderresources.finish();
for (const Stage::Texture &t : qAsConst(res[RBM_COMPUTE].textures))
srbD->csshaderresources.feed(t.treg, t.srv);
for (const Stage::Sampler &s : qAsConst(res[RBM_COMPUTE].samplers))
srbD->cssamplers.feed(s.sreg, s.sampler);
srbD->cssamplersPresent = srbD->cssamplers.finish();
srbD->csshaderresources.finish();
for (const Stage::Uav &u : qAsConst(res[RBM_COMPUTE].uavs))
srbD->csUAVs.feed(u.ureg, u.uav);
srbD->csUAVsPresent = srbD->csUAVs.finish();
res[RBM_COMPUTE].buildUavBatches(srbD->csUavBatches);
}
void QRhiD3D11::executeBufferHostWrites(QD3D11Buffer *bufD)
@ -2373,8 +2393,8 @@ void QRhiD3D11::executeBufferHostWrites(QD3D11Buffer *bufD)
static void applyDynamicOffsets(UINT *offsets,
int batchIndex,
QRhiBatchedBindings<UINT> *originalBindings,
QRhiBatchedBindings<UINT> *staticOffsets,
const QRhiBatchedBindings<UINT> *originalBindings,
const QRhiBatchedBindings<UINT> *staticOffsets,
const uint *dynOfsPairs, int dynOfsPairCount)
{
const int count = staticOffsets->batches[batchIndex].resources.count();
@ -2405,162 +2425,92 @@ static inline uint clampedResourceCount(uint startSlot, int countSlots, uint max
return countSlots;
}
#define SETUBUFBATCH(stagePrefixL, stagePrefixU) \
if (srbD->stagePrefixL##UniformBufferBatches.present) { \
const QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches(srbD->stagePrefixL##UniformBufferBatches); \
for (int i = 0, ie = batches.ubufs.batches.count(); i != ie; ++i) { \
const uint count = clampedResourceCount(batches.ubufs.batches[i].startBinding, \
batches.ubufs.batches[i].resources.count(), \
D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, \
#stagePrefixU " cbuf"); \
if (count) { \
if (!dynOfsPairCount) { \
context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding, \
count, \
batches.ubufs.batches[i].resources.constData(), \
batches.ubufoffsets.batches[i].resources.constData(), \
batches.ubufsizes.batches[i].resources.constData()); \
} else { \
applyDynamicOffsets(offsets, i, \
&batches.ubuforigbindings, &batches.ubufoffsets, \
dynOfsPairs, dynOfsPairCount); \
context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding, \
count, \
batches.ubufs.batches[i].resources.constData(), \
offsets, \
batches.ubufsizes.batches[i].resources.constData()); \
} \
} \
} \
}
#define SETSAMPLERBATCH(stagePrefixL, stagePrefixU) \
if (srbD->stagePrefixL##SamplerBatches.present) { \
for (const auto &batch : srbD->stagePrefixL##SamplerBatches.samplers.batches) { \
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), \
D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, #stagePrefixU " sampler"); \
if (count) \
context->stagePrefixU##SetSamplers(batch.startBinding, count, batch.resources.constData()); \
} \
for (const auto &batch : srbD->stagePrefixL##SamplerBatches.shaderresources.batches) { \
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), \
D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, #stagePrefixU " SRV"); \
if (count) { \
context->stagePrefixU##SetShaderResources(batch.startBinding, count, batch.resources.constData()); \
contextState.stagePrefixL##HighestActiveSrvBinding = qMax(contextState.stagePrefixL##HighestActiveSrvBinding, \
int(batch.startBinding + count) - 1); \
} \
} \
}
#define SETUAVBATCH(stagePrefixL, stagePrefixU) \
if (srbD->stagePrefixL##UavBatches.present) { \
for (const auto &batch : srbD->stagePrefixL##UavBatches.uavs.batches) { \
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), \
D3D11_1_UAV_SLOT_COUNT, #stagePrefixU " UAV"); \
if (count) { \
context->stagePrefixU##SetUnorderedAccessViews(batch.startBinding, \
count, \
batch.resources.constData(), \
nullptr); \
contextState.stagePrefixL##HighestActiveUavBinding = qMax(contextState.stagePrefixL##HighestActiveUavBinding, \
int(batch.startBinding + count) - 1); \
} \
} \
}
void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
const uint *dynOfsPairs, int dynOfsPairCount,
bool offsetOnlyChange)
{
UINT offsets[QD3D11CommandBuffer::MAX_DYNAMIC_OFFSET_COUNT];
if (srbD->vsubufsPresent) {
for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) {
const uint count = clampedResourceCount(srbD->vsubufs.batches[i].startBinding,
srbD->vsubufs.batches[i].resources.count(),
D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
"VS cbuf");
if (count) {
if (!dynOfsPairCount) {
context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
count,
srbD->vsubufs.batches[i].resources.constData(),
srbD->vsubufoffsets.batches[i].resources.constData(),
srbD->vsubufsizes.batches[i].resources.constData());
} else {
applyDynamicOffsets(offsets, i, &srbD->vsubuforigbindings, &srbD->vsubufoffsets,
dynOfsPairs, dynOfsPairCount);
context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
count,
srbD->vsubufs.batches[i].resources.constData(),
offsets,
srbD->vsubufsizes.batches[i].resources.constData());
}
}
}
}
if (srbD->fsubufsPresent) {
for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) {
const uint count = clampedResourceCount(srbD->fsubufs.batches[i].startBinding,
srbD->fsubufs.batches[i].resources.count(),
D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
"PS cbuf");
if (count) {
if (!dynOfsPairCount) {
context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
count,
srbD->fsubufs.batches[i].resources.constData(),
srbD->fsubufoffsets.batches[i].resources.constData(),
srbD->fsubufsizes.batches[i].resources.constData());
} else {
applyDynamicOffsets(offsets, i, &srbD->fsubuforigbindings, &srbD->fsubufoffsets,
dynOfsPairs, dynOfsPairCount);
context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
count,
srbD->fsubufs.batches[i].resources.constData(),
offsets,
srbD->fsubufsizes.batches[i].resources.constData());
}
}
}
}
if (srbD->csubufsPresent) {
for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) {
const uint count = clampedResourceCount(srbD->csubufs.batches[i].startBinding,
srbD->csubufs.batches[i].resources.count(),
D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
"CS cbuf");
if (count) {
if (!dynOfsPairCount) {
context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
count,
srbD->csubufs.batches[i].resources.constData(),
srbD->csubufoffsets.batches[i].resources.constData(),
srbD->csubufsizes.batches[i].resources.constData());
} else {
applyDynamicOffsets(offsets, i, &srbD->csubuforigbindings, &srbD->csubufoffsets,
dynOfsPairs, dynOfsPairCount);
context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
count,
srbD->csubufs.batches[i].resources.constData(),
offsets,
srbD->csubufsizes.batches[i].resources.constData());
}
}
}
}
SETUBUFBATCH(vs, VS)
SETUBUFBATCH(hs, HS)
SETUBUFBATCH(ds, DS)
SETUBUFBATCH(gs, GS)
SETUBUFBATCH(fs, PS)
SETUBUFBATCH(cs, CS)
if (!offsetOnlyChange) {
if (srbD->vssamplersPresent) {
for (const auto &batch : srbD->vssamplers.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "VS sampler");
if (count)
context->VSSetSamplers(batch.startBinding, count, batch.resources.constData());
}
SETSAMPLERBATCH(vs, VS)
SETSAMPLERBATCH(hs, HS)
SETSAMPLERBATCH(ds, DS)
SETSAMPLERBATCH(gs, GS)
SETSAMPLERBATCH(fs, PS)
SETSAMPLERBATCH(cs, CS)
for (const auto &batch : srbD->vsshaderresources.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "VS SRV");
if (count) {
context->VSSetShaderResources(batch.startBinding, count, batch.resources.constData());
contextState.vsHighestActiveSrvBinding = qMax(contextState.vsHighestActiveSrvBinding,
int(batch.startBinding + count) - 1);
}
}
}
if (srbD->fssamplersPresent) {
for (const auto &batch : srbD->fssamplers.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "PS sampler");
if (count)
context->PSSetSamplers(batch.startBinding, count, batch.resources.constData());
}
for (const auto &batch : srbD->fsshaderresources.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "PS SRV");
if (count) {
context->PSSetShaderResources(batch.startBinding, count, batch.resources.constData());
contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding,
int(batch.startBinding + count) - 1);
}
}
}
if (srbD->cssamplersPresent) {
for (const auto &batch : srbD->cssamplers.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "CS sampler");
if (count)
context->CSSetSamplers(batch.startBinding, count, batch.resources.constData());
}
for (const auto &batch : srbD->csshaderresources.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "CS SRV");
if (count) {
context->CSSetShaderResources(batch.startBinding, count, batch.resources.constData());
contextState.csHighestActiveSrvBinding = qMax(contextState.csHighestActiveSrvBinding,
int(batch.startBinding + count) - 1);
}
}
}
if (srbD->csUAVsPresent) {
for (const auto &batch : srbD->csUAVs.batches) {
const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
D3D11_1_UAV_SLOT_COUNT, "CS UAV");
if (count) {
context->CSSetUnorderedAccessViews(batch.startBinding,
count,
batch.resources.constData(),
nullptr);
contextState.csHighestActiveUavBinding = qMax(contextState.csHighestActiveUavBinding,
int(batch.startBinding + count) - 1);
}
}
}
SETUAVBATCH(cs, CS)
}
}
@ -2589,6 +2539,9 @@ void QRhiD3D11::resetShaderResources()
}
int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding);
nullsrvCount = qMax(nullsrvCount, contextState.hsHighestActiveSrvBinding);
nullsrvCount = qMax(nullsrvCount, contextState.dsHighestActiveSrvBinding);
nullsrvCount = qMax(nullsrvCount, contextState.gsHighestActiveSrvBinding);
nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding);
nullsrvCount += 1;
if (nullsrvCount > 0) {
@ -2600,6 +2553,18 @@ void QRhiD3D11::resetShaderResources()
context->VSSetShaderResources(0, UINT(contextState.vsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.vsHighestActiveSrvBinding = -1;
}
if (contextState.hsHighestActiveSrvBinding >= 0) {
context->HSSetShaderResources(0, UINT(contextState.hsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.hsHighestActiveSrvBinding = -1;
}
if (contextState.dsHighestActiveSrvBinding >= 0) {
context->DSSetShaderResources(0, UINT(contextState.dsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.dsHighestActiveSrvBinding = -1;
}
if (contextState.gsHighestActiveSrvBinding >= 0) {
context->GSSetShaderResources(0, UINT(contextState.gsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.gsHighestActiveSrvBinding = -1;
}
if (contextState.fsHighestActiveSrvBinding >= 0) {
context->PSSetShaderResources(0, UINT(contextState.fsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.fsHighestActiveSrvBinding = -1;
@ -2621,10 +2586,27 @@ void QRhiD3D11::resetShaderResources()
}
}
#define SETSHADER(StageL, StageU) \
if (psD->StageL.shader) { \
context->StageU##SetShader(psD->StageL.shader, nullptr, 0); \
currentShaderMask |= StageU##MaskBit; \
} else if (currentShaderMask & StageU##MaskBit) { \
context->StageU##SetShader(nullptr, nullptr, 0); \
currentShaderMask &= ~StageU##MaskBit; \
}
void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain)
{
quint32 stencilRef = 0;
float blendConstants[] = { 1, 1, 1, 1 };
enum ActiveShaderMask {
VSMaskBit = 0x01,
HSMaskBit = 0x02,
DSMaskBit = 0x04,
GSMaskBit = 0x08,
PSMaskBit = 0x10
};
int currentShaderMask = 0xFF;
if (timestampSwapChain) {
const int currentFrameSlot = timestampSwapChain->currentFrameSlot;
@ -2713,8 +2695,11 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *
case QD3D11CommandBuffer::Command::BindGraphicsPipeline:
{
QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
context->VSSetShader(psD->vs.shader, nullptr, 0);
context->PSSetShader(psD->fs.shader, nullptr, 0);
SETSHADER(vs, VS)
SETSHADER(hs, HS)
SETSHADER(ds, DS)
SETSHADER(gs, GS)
SETSHADER(fs, PS)
context->IASetPrimitiveTopology(psD->d3dTopology);
context->IASetInputLayout(psD->inputLayout); // may be null, that's ok
context->OMSetDepthStencilState(psD->dsState, stencilRef);
@ -3853,6 +3838,16 @@ QD3D11GraphicsPipeline::~QD3D11GraphicsPipeline()
destroy();
}
template<typename T>
inline void releasePipelineShader(T &s)
{
if (s.shader) {
s.shader->Release();
s.shader = nullptr;
}
s.nativeResourceBindingMap.clear();
}
void QD3D11GraphicsPipeline::destroy()
{
if (!dsState)
@ -3876,17 +3871,11 @@ void QD3D11GraphicsPipeline::destroy()
rastState = nullptr;
}
if (vs.shader) {
vs.shader->Release();
vs.shader = nullptr;
}
vs.nativeResourceBindingMap.clear();
if (fs.shader) {
fs.shader->Release();
fs.shader = nullptr;
}
fs.nativeResourceBindingMap.clear();
releasePipelineShader(vs);
releasePipelineShader(hs);
releasePipelineShader(ds);
releasePipelineShader(gs);
releasePipelineShader(fs);
QRHI_RES_RHI(QRhiD3D11);
if (rhiD)
@ -4010,7 +3999,7 @@ static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format
}
}
static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t)
static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
{
switch (t) {
case QRhiGraphicsPipeline::Triangles:
@ -4023,6 +4012,9 @@ static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topol
return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
case QRhiGraphicsPipeline::Points:
return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
case QRhiGraphicsPipeline::Patches:
Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
return D3D11_PRIMITIVE_TOPOLOGY(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
default:
Q_UNREACHABLE();
return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
@ -4307,6 +4299,21 @@ bool QD3D11GraphicsPipeline::create()
vsByteCode = cacheIt->bytecode;
vs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
break;
case QRhiShaderStage::TessellationControl:
hs.shader = static_cast<ID3D11HullShader *>(cacheIt->s);
hs.shader->AddRef();
hs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
break;
case QRhiShaderStage::TessellationEvaluation:
ds.shader = static_cast<ID3D11DomainShader *>(cacheIt->s);
ds.shader->AddRef();
ds.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
break;
case QRhiShaderStage::Geometry:
gs.shader = static_cast<ID3D11GeometryShader *>(cacheIt->s);
gs.shader->AddRef();
gs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
break;
case QRhiShaderStage::Fragment:
fs.shader = static_cast<ID3D11PixelShader *>(cacheIt->s);
fs.shader->AddRef();
@ -4346,6 +4353,36 @@ bool QD3D11GraphicsPipeline::create()
rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(vs.shader, bytecode, vs.nativeResourceBindingMap));
vs.shader->AddRef();
break;
case QRhiShaderStage::TessellationControl:
hr = rhiD->dev->CreateHullShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &hs.shader);
if (FAILED(hr)) {
qWarning("Failed to create hull shader: %s", qPrintable(comErrorMessage(hr)));
return false;
}
hs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(hs.shader, bytecode, hs.nativeResourceBindingMap));
hs.shader->AddRef();
break;
case QRhiShaderStage::TessellationEvaluation:
hr = rhiD->dev->CreateDomainShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &ds.shader);
if (FAILED(hr)) {
qWarning("Failed to create domain shader: %s", qPrintable(comErrorMessage(hr)));
return false;
}
ds.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(ds.shader, bytecode, ds.nativeResourceBindingMap));
ds.shader->AddRef();
break;
case QRhiShaderStage::Geometry:
hr = rhiD->dev->CreateGeometryShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &gs.shader);
if (FAILED(hr)) {
qWarning("Failed to create geometry shader: %s", qPrintable(comErrorMessage(hr)));
return false;
}
gs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(gs.shader, bytecode, gs.nativeResourceBindingMap));
gs.shader->AddRef();
break;
case QRhiShaderStage::Fragment:
hr = rhiD->dev->CreatePixelShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &fs.shader);
if (FAILED(hr)) {
@ -4362,7 +4399,7 @@ bool QD3D11GraphicsPipeline::create()
}
}
d3dTopology = toD3DTopology(m_topology);
d3dTopology = toD3DTopology(m_topology, m_patchControlPointCount);
if (!vsByteCode.isEmpty()) {
QByteArrayList matrixSliceSemantics;

View File

@ -221,39 +221,66 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
};
QVarLengthArray<BoundResourceData, 8> boundResourceData;
bool vsubufsPresent = false;
bool fsubufsPresent = false;
bool csubufsPresent = false;
bool vssamplersPresent = false;
bool fssamplersPresent = false;
bool cssamplersPresent = false;
bool csUAVsPresent = false;
struct StageUniformBufferBatches {
bool present = false;
QRhiBatchedBindings<ID3D11Buffer *> ubufs;
QRhiBatchedBindings<UINT> ubuforigbindings;
QRhiBatchedBindings<UINT> ubufoffsets;
QRhiBatchedBindings<UINT> ubufsizes;
void finish() {
present = ubufs.finish();
ubuforigbindings.finish();
ubufoffsets.finish();
ubufsizes.finish();
}
void clear() {
ubufs.clear();
ubuforigbindings.clear();
ubufoffsets.clear();
ubufsizes.clear();
}
};
QRhiBatchedBindings<ID3D11Buffer *> vsubufs;
QRhiBatchedBindings<UINT> vsubuforigbindings;
QRhiBatchedBindings<UINT> vsubufoffsets;
QRhiBatchedBindings<UINT> vsubufsizes;
struct StageSamplerBatches {
bool present = false;
QRhiBatchedBindings<ID3D11SamplerState *> samplers;
QRhiBatchedBindings<ID3D11ShaderResourceView *> shaderresources;
void finish() {
present = samplers.finish();
shaderresources.finish();
}
void clear() {
samplers.clear();
shaderresources.clear();
}
};
QRhiBatchedBindings<ID3D11Buffer *> fsubufs;
QRhiBatchedBindings<UINT> fsubuforigbindings;
QRhiBatchedBindings<UINT> fsubufoffsets;
QRhiBatchedBindings<UINT> fsubufsizes;
struct StageUavBatches {
bool present = false;
QRhiBatchedBindings<ID3D11UnorderedAccessView *> uavs;
void finish() {
present = uavs.finish();
}
void clear() {
uavs.clear();
}
};
QRhiBatchedBindings<ID3D11Buffer *> csubufs;
QRhiBatchedBindings<UINT> csubuforigbindings;
QRhiBatchedBindings<UINT> csubufoffsets;
QRhiBatchedBindings<UINT> csubufsizes;
StageUniformBufferBatches vsUniformBufferBatches;
StageUniformBufferBatches hsUniformBufferBatches;
StageUniformBufferBatches dsUniformBufferBatches;
StageUniformBufferBatches gsUniformBufferBatches;
StageUniformBufferBatches fsUniformBufferBatches;
StageUniformBufferBatches csUniformBufferBatches;
QRhiBatchedBindings<ID3D11SamplerState *> vssamplers;
QRhiBatchedBindings<ID3D11ShaderResourceView *> vsshaderresources;
StageSamplerBatches vsSamplerBatches;
StageSamplerBatches hsSamplerBatches;
StageSamplerBatches dsSamplerBatches;
StageSamplerBatches gsSamplerBatches;
StageSamplerBatches fsSamplerBatches;
StageSamplerBatches csSamplerBatches;
QRhiBatchedBindings<ID3D11SamplerState *> fssamplers;
QRhiBatchedBindings<ID3D11ShaderResourceView *> fsshaderresources;
QRhiBatchedBindings<ID3D11SamplerState *> cssamplers;
QRhiBatchedBindings<ID3D11ShaderResourceView *> csshaderresources;
QRhiBatchedBindings<ID3D11UnorderedAccessView *> csUAVs;
StageUavBatches csUavBatches;
friend class QRhiD3D11;
};
@ -273,6 +300,18 @@ struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline
ID3D11VertexShader *shader = nullptr;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
} vs;
struct {
ID3D11HullShader *shader = nullptr;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
} hs;
struct {
ID3D11DomainShader *shader = nullptr;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
} ds;
struct {
ID3D11GeometryShader *shader = nullptr;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
} gs;
struct {
ID3D11PixelShader *shader = nullptr;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
@ -707,6 +746,9 @@ public:
int vsHighestActiveVertexBufferBinding = -1;
bool vsHasIndexBufferBound = false;
int vsHighestActiveSrvBinding = -1;
int hsHighestActiveSrvBinding = -1;
int dsHighestActiveSrvBinding = -1;
int gsHighestActiveSrvBinding = -1;
int fsHighestActiveSrvBinding = -1;
int csHighestActiveSrvBinding = -1;
int csHighestActiveUavBinding = -1;

View File

@ -1,3 +1,4 @@
qsb --glsl 320es,410 test.vert -o test.vert.qsb
qsb --glsl 320es,410 --hlsl 50 test.vert -o test.vert.qsb
qsb --glsl 320es,410 test.geom -o test.geom.qsb
qsb --glsl 320es,410 test.frag -o test.frag.qsb
qsb -r hlsl,50,test_geom.hlsl test.geom.qsb
qsb --glsl 320es,410 --hlsl 50 test.frag -o test.frag.qsb

View File

@ -0,0 +1,26 @@
struct VertexOutput
{
float4 position : SV_Position;
};
struct PixelInput
{
float4 position : SV_POSITION;
};
cbuffer buf : register(b0)
{
float radius : packoffset(c0);
};
[maxvertexcount(7)]
void main(point VertexOutput input[1], inout LineStream<PixelInput> OutputStream)
{
PixelInput output;
for (int i = 0; i < 7; ++i) {
float theta = float(i) / 6.0f * 2.0 * 3.14159265;
output.position = input[0].position;
output.position.xy += radius * float2(cos(theta), sin(theta));
OutputStream.Append(output);
}
}

View File

@ -1,4 +1,6 @@
qsb --glsl 320es,410 test.vert -o test.vert.qsb
qsb --glsl 320es,410 --hlsl 50 test.vert -o test.vert.qsb
qsb --glsl 320es,410 test.tesc -o test.tesc.qsb
qsb -r hlsl,50,test_hull.hlsl test.tesc.qsb
qsb --glsl 320es,410 test.tese -o test.tese.qsb
qsb --glsl 320es,410 test.frag -o test.frag.qsb
qsb -r hlsl,50,test_domain.hlsl test.tese.qsb
qsb --glsl 320es,410 --hlsl 50 test.frag -o test.frag.qsb

View File

@ -0,0 +1,38 @@
struct Input
{
float edges[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
struct PatchInput
{
float3 position : POSITION;
float3 color : COLOR;
};
struct PixelInput
{
float3 color : TEXCOORD0;
float4 position : SV_POSITION;
};
cbuffer buf : register(b0)
{
row_major float4x4 mvp : packoffset(c0);
float time : packoffset(c4);
float amplitude : packoffset(c4.y);
};
[domain("tri")]
PixelInput main(Input input, float3 uvwCoord : SV_DomainLocation, const OutputPatch<PatchInput, 3> patch)
{
PixelInput output;
float3 vertexPosition = uvwCoord.x * patch[0].position + uvwCoord.y * patch[1].position + uvwCoord.z * patch[2].position;
output.position = mul(float4(vertexPosition, 1.0f), mvp);
output.position.x += sin(time + output.position.y) * amplitude;
output.color = uvwCoord.x * patch[0].color + uvwCoord.y * patch[1].color + uvwCoord.z * patch[2].color;
return output;
}

View File

@ -0,0 +1,40 @@
struct Input
{
float3 color : TEXCOORD0;
float4 position : SV_Position;
};
struct Output
{
float3 position : POSITION;
float3 color : COLOR;
};
struct ConstantData
{
float edges[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
ConstantData patchConstFunc(InputPatch<Input, 3> ip, uint PatchID : SV_PrimitiveID )
{
ConstantData d;
d.edges[0] = 4.0;
d.edges[1] = 4.0;
d.edges[2] = 4.0;
d.inside = 4.0;
return d;
}
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("patchConstFunc")]
Output main(InputPatch<Input, 3> patch, uint pointId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID)
{
Output output;
output.position = patch[pointId].position;
output.color = patch[pointId].color;
return output;
}