mirror of https://github.com/AxioDL/boo.git
OpenGL and Vulkan support for tessellation shaders
This commit is contained in:
parent
82966931f8
commit
4a19ac1e83
|
@ -54,6 +54,14 @@ public:
|
||||||
ZTest depthTest, bool depthWrite, bool colorWrite,
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
bool alphaWrite, CullMode culling, bool overwriteAlpha = true);
|
bool alphaWrite, CullMode culling, bool overwriteAlpha = true);
|
||||||
|
|
||||||
|
ObjToken<IShaderPipeline> newTessellationShaderPipeline(const char* vertSource, const char* fragSource,
|
||||||
|
const char* controlSource, const char* evaluationSource,
|
||||||
|
size_t texCount, const char** texNames,
|
||||||
|
size_t uniformBlockCount, const char** uniformBlockNames,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, CullMode culling, bool overwriteAlpha = true);
|
||||||
|
|
||||||
ObjToken<IShaderDataBinding>
|
ObjToken<IShaderDataBinding>
|
||||||
newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline,
|
newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline,
|
||||||
const ObjToken<IVertexFormat>& vtxFormat,
|
const ObjToken<IVertexFormat>& vtxFormat,
|
||||||
|
|
|
@ -71,6 +71,7 @@ enum class TextureClampMode
|
||||||
{
|
{
|
||||||
Repeat,
|
Repeat,
|
||||||
ClampToWhite,
|
ClampToWhite,
|
||||||
|
ClampToBlack,
|
||||||
ClampToEdge,
|
ClampToEdge,
|
||||||
ClampToEdgeNearest
|
ClampToEdgeNearest
|
||||||
};
|
};
|
||||||
|
@ -176,7 +177,8 @@ enum class PipelineStage
|
||||||
enum class Primitive
|
enum class Primitive
|
||||||
{
|
{
|
||||||
Triangles,
|
Triangles,
|
||||||
TriStrips
|
TriStrips,
|
||||||
|
Patches /* Do not use directly, construct a tessellation pipeline instead */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Used by platform shader pipeline constructors */
|
/** Used by platform shader pipeline constructors */
|
||||||
|
@ -293,10 +295,9 @@ struct IGraphicsDataFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void commitTransaction(const std::function<bool(Context& ctx)>& __BooTraceArgs)=0;
|
virtual void commitTransaction(const std::function<bool(Context& ctx)>& __BooTraceArgs)=0;
|
||||||
|
|
||||||
virtual ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs)=0;
|
virtual ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs)=0;
|
||||||
|
|
||||||
virtual void setDisplayGamma(float gamma)=0;
|
virtual void setDisplayGamma(float gamma)=0;
|
||||||
|
virtual bool isTessellationSupported(uint32_t& maxPatchSizeOut)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
using GraphicsDataFactoryContext = IGraphicsDataFactory::Context;
|
using GraphicsDataFactoryContext = IGraphicsDataFactory::Context;
|
||||||
|
|
|
@ -181,6 +181,31 @@ public:
|
||||||
colorWrite, alphaWrite, culling);
|
colorWrite, alphaWrite, culling);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boo::ObjToken<IShaderPipeline> newTessellationShaderPipeline(const char* vertSource, const char* fragSource,
|
||||||
|
const char* controlSource, const char* evaluationSource,
|
||||||
|
std::vector<unsigned int>* vertBlobOut,
|
||||||
|
std::vector<unsigned int>* fragBlobOut,
|
||||||
|
std::vector<unsigned int>* controlBlobOut,
|
||||||
|
std::vector<unsigned int>* evaluationBlobOut,
|
||||||
|
std::vector<unsigned char>* pipelineBlob,
|
||||||
|
const boo::ObjToken<IVertexFormat>& vtxFmt,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, CullMode culling, bool overwriteAlpha = true);
|
||||||
|
|
||||||
|
boo::ObjToken<IShaderPipeline> newTessellationShaderPipeline(const char* vertSource, const char* fragSource,
|
||||||
|
const char* controlSource, const char* evaluationSource,
|
||||||
|
const boo::ObjToken<IVertexFormat>& vtxFmt,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, CullMode culling)
|
||||||
|
{
|
||||||
|
return newTessellationShaderPipeline(vertSource, fragSource, controlSource, evaluationSource,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
vtxFmt, srcFac, dstFac, patchSize, depthTest, depthWrite,
|
||||||
|
colorWrite, alphaWrite, culling);
|
||||||
|
}
|
||||||
|
|
||||||
boo::ObjToken<IShaderDataBinding>
|
boo::ObjToken<IShaderDataBinding>
|
||||||
newShaderDataBinding(const boo::ObjToken<IShaderPipeline>& pipeline,
|
newShaderDataBinding(const boo::ObjToken<IShaderPipeline>& pipeline,
|
||||||
const boo::ObjToken<IVertexFormat>& vtxFormat,
|
const boo::ObjToken<IVertexFormat>& vtxFormat,
|
||||||
|
|
|
@ -76,6 +76,9 @@ class GLDataFactoryImpl : public GLDataFactory, public GraphicsDataFactoryHead
|
||||||
GLContext* m_glCtx;
|
GLContext* m_glCtx;
|
||||||
std::unordered_map<uint64_t, std::unique_ptr<GLShareableShader>> m_sharedShaders;
|
std::unordered_map<uint64_t, std::unique_ptr<GLShareableShader>> m_sharedShaders;
|
||||||
|
|
||||||
|
bool m_hasTessellation = false;
|
||||||
|
uint32_t m_maxPatchSize = 0;
|
||||||
|
|
||||||
float m_gamma = 1.f;
|
float m_gamma = 1.f;
|
||||||
ObjToken<IShaderPipeline> m_gammaShader;
|
ObjToken<IShaderPipeline> m_gammaShader;
|
||||||
ObjToken<ITextureD> m_gammaLUT;
|
ObjToken<ITextureD> m_gammaLUT;
|
||||||
|
@ -83,6 +86,15 @@ class GLDataFactoryImpl : public GLDataFactory, public GraphicsDataFactoryHead
|
||||||
ObjToken<IVertexFormat> m_gammaVFMT;
|
ObjToken<IVertexFormat> m_gammaVFMT;
|
||||||
void SetupGammaResources()
|
void SetupGammaResources()
|
||||||
{
|
{
|
||||||
|
/* Good enough place for this */
|
||||||
|
if (GLEW_ARB_tessellation_shader)
|
||||||
|
{
|
||||||
|
m_hasTessellation = true;
|
||||||
|
GLint maxPVerts;
|
||||||
|
glGetIntegerv(GL_MAX_PATCH_VERTICES, &maxPVerts);
|
||||||
|
m_maxPatchSize = uint32_t(maxPVerts);
|
||||||
|
}
|
||||||
|
|
||||||
commitTransaction([this](IGraphicsDataFactory::Context& ctx)
|
commitTransaction([this](IGraphicsDataFactory::Context& ctx)
|
||||||
{
|
{
|
||||||
const char* texNames[] = {"screenTex", "gammaLUT"};
|
const char* texNames[] = {"screenTex", "gammaLUT"};
|
||||||
|
@ -125,6 +137,14 @@ public:
|
||||||
if (gamma != 1.f)
|
if (gamma != 1.f)
|
||||||
UpdateGammaLUT(m_gammaLUT.get(), gamma);
|
UpdateGammaLUT(m_gammaLUT.get(), gamma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isTessellationSupported(uint32_t& maxPatchSizeOut)
|
||||||
|
{
|
||||||
|
maxPatchSizeOut = m_maxPatchSize;
|
||||||
|
return m_hasTessellation;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLShareableShader::Token PrepareShaderStage(const char* source, GLenum stage);
|
||||||
};
|
};
|
||||||
|
|
||||||
static const GLenum USE_TABLE[] =
|
static const GLenum USE_TABLE[] =
|
||||||
|
@ -250,6 +270,15 @@ static void SetClampMode(GLenum target, TextureClampMode clampMode)
|
||||||
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
|
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureClampMode::ClampToBlack:
|
||||||
|
{
|
||||||
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
|
||||||
|
GLfloat color[] = {0.f, 0.f, 0.f, 1.f};
|
||||||
|
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TextureClampMode::ClampToEdge:
|
case TextureClampMode::ClampToEdge:
|
||||||
{
|
{
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
@ -653,8 +682,32 @@ GLDataFactory::Context::newStaticArrayTexture(size_t width, size_t height, size_
|
||||||
factory.m_glCtx->m_anisotropy, data, sz)};
|
factory.m_glCtx->m_anisotropy, data, sz)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const GLenum PRIMITIVE_TABLE[] =
|
||||||
|
{
|
||||||
|
GL_TRIANGLES,
|
||||||
|
GL_TRIANGLE_STRIP,
|
||||||
|
GL_PATCHES
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GLenum BLEND_FACTOR_TABLE[] =
|
||||||
|
{
|
||||||
|
GL_ZERO,
|
||||||
|
GL_ONE,
|
||||||
|
GL_SRC_COLOR,
|
||||||
|
GL_ONE_MINUS_SRC_COLOR,
|
||||||
|
GL_DST_COLOR,
|
||||||
|
GL_ONE_MINUS_DST_COLOR,
|
||||||
|
GL_SRC_ALPHA,
|
||||||
|
GL_ONE_MINUS_SRC_ALPHA,
|
||||||
|
GL_DST_ALPHA,
|
||||||
|
GL_ONE_MINUS_DST_ALPHA,
|
||||||
|
GL_SRC1_COLOR,
|
||||||
|
GL_ONE_MINUS_SRC1_COLOR
|
||||||
|
};
|
||||||
|
|
||||||
class GLShaderPipeline : public GraphicsDataNode<IShaderPipeline>
|
class GLShaderPipeline : public GraphicsDataNode<IShaderPipeline>
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
friend class GLDataFactory;
|
friend class GLDataFactory;
|
||||||
friend struct GLCommandQueue;
|
friend struct GLCommandQueue;
|
||||||
friend struct GLShaderDataBinding;
|
friend struct GLShaderDataBinding;
|
||||||
|
@ -674,11 +727,50 @@ class GLShaderPipeline : public GraphicsDataNode<IShaderPipeline>
|
||||||
mutable std::vector<GLint> m_uniLocs;
|
mutable std::vector<GLint> m_uniLocs;
|
||||||
mutable std::vector<std::string> m_texNames;
|
mutable std::vector<std::string> m_texNames;
|
||||||
mutable std::vector<std::string> m_blockNames;
|
mutable std::vector<std::string> m_blockNames;
|
||||||
GLShaderPipeline(const ObjToken<BaseGraphicsData>& parent)
|
GLShaderPipeline(const ObjToken<BaseGraphicsData>& parent,
|
||||||
: GraphicsDataNode<IShaderPipeline>(parent) {}
|
size_t texCount, const char** texNames,
|
||||||
|
size_t uniformBlockCount, const char** uniformBlockNames,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, Primitive prim,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, CullMode culling, bool overwriteAlpha)
|
||||||
|
: GraphicsDataNode<IShaderPipeline>(parent)
|
||||||
|
{
|
||||||
|
m_texNames.reserve(texCount);
|
||||||
|
for (int i=0 ; i<texCount ; ++i)
|
||||||
|
m_texNames.emplace_back(texNames[i]);
|
||||||
|
|
||||||
|
m_blockNames.reserve(uniformBlockCount);
|
||||||
|
for (int i=0 ; i<uniformBlockCount ; ++i)
|
||||||
|
m_blockNames.emplace_back(uniformBlockNames[i]);
|
||||||
|
|
||||||
|
if (srcFac == BlendFactor::Subtract || dstFac == BlendFactor::Subtract)
|
||||||
|
{
|
||||||
|
m_sfactor = GL_SRC_ALPHA;
|
||||||
|
m_dfactor = GL_ONE;
|
||||||
|
m_subtractBlend = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_sfactor = BLEND_FACTOR_TABLE[int(srcFac)];
|
||||||
|
m_dfactor = BLEND_FACTOR_TABLE[int(dstFac)];
|
||||||
|
m_subtractBlend = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_depthTest = depthTest;
|
||||||
|
m_depthWrite = depthWrite;
|
||||||
|
m_colorWrite = colorWrite;
|
||||||
|
m_alphaWrite = alphaWrite;
|
||||||
|
m_overwriteAlpha = overwriteAlpha;
|
||||||
|
m_culling = culling;
|
||||||
|
m_drawPrim = PRIMITIVE_TABLE[int(prim)];
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
~GLShaderPipeline() { if (m_prog) glDeleteProgram(m_prog); }
|
~GLShaderPipeline() { if (m_prog) glDeleteProgram(m_prog); }
|
||||||
|
|
||||||
|
virtual void attachExtraStages() const {}
|
||||||
|
virtual void resetExtraStages() const {}
|
||||||
|
virtual void setExtraParameters() const {}
|
||||||
|
|
||||||
GLuint bind() const
|
GLuint bind() const
|
||||||
{
|
{
|
||||||
if (!m_prog)
|
if (!m_prog)
|
||||||
|
@ -692,6 +784,7 @@ public:
|
||||||
|
|
||||||
glAttachShader(m_prog, m_vert.get().m_shader);
|
glAttachShader(m_prog, m_vert.get().m_shader);
|
||||||
glAttachShader(m_prog, m_frag.get().m_shader);
|
glAttachShader(m_prog, m_frag.get().m_shader);
|
||||||
|
attachExtraStages();
|
||||||
|
|
||||||
glLinkProgram(m_prog);
|
glLinkProgram(m_prog);
|
||||||
|
|
||||||
|
@ -700,6 +793,7 @@ public:
|
||||||
|
|
||||||
m_vert.reset();
|
m_vert.reset();
|
||||||
m_frag.reset();
|
m_frag.reset();
|
||||||
|
resetExtraStages();
|
||||||
|
|
||||||
GLint status;
|
GLint status;
|
||||||
glGetProgramiv(m_prog, GL_LINK_STATUS, &status);
|
glGetProgramiv(m_prog, GL_LINK_STATUS, &status);
|
||||||
|
@ -795,31 +889,94 @@ public:
|
||||||
else
|
else
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
|
|
||||||
|
setExtraParameters();
|
||||||
|
|
||||||
return m_prog;
|
return m_prog;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const GLenum PRIMITIVE_TABLE[] =
|
class GLTessellationShaderPipeline : public GLShaderPipeline
|
||||||
{
|
{
|
||||||
GL_TRIANGLES,
|
friend class GLDataFactory;
|
||||||
GL_TRIANGLE_STRIP
|
friend struct GLCommandQueue;
|
||||||
|
friend struct GLShaderDataBinding;
|
||||||
|
GLint m_patchSize;
|
||||||
|
mutable GLShareableShader::Token m_control;
|
||||||
|
mutable GLShareableShader::Token m_evaluation;
|
||||||
|
GLTessellationShaderPipeline(const ObjToken<BaseGraphicsData>& parent,
|
||||||
|
size_t texCount, const char** texNames,
|
||||||
|
size_t uniformBlockCount, const char** uniformBlockNames,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, CullMode culling, bool overwriteAlpha)
|
||||||
|
: GLShaderPipeline(parent, texCount, texNames, uniformBlockCount, uniformBlockNames,
|
||||||
|
srcFac, dstFac, Primitive::Patches, depthTest, depthWrite, colorWrite,
|
||||||
|
alphaWrite, culling, overwriteAlpha), m_patchSize(patchSize)
|
||||||
|
{}
|
||||||
|
public:
|
||||||
|
~GLTessellationShaderPipeline() = default;
|
||||||
|
|
||||||
|
void attachExtraStages() const
|
||||||
|
{
|
||||||
|
glAttachShader(m_prog, m_control.get().m_shader);
|
||||||
|
glAttachShader(m_prog, m_evaluation.get().m_shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetExtraStages() const
|
||||||
|
{
|
||||||
|
glDetachShader(m_prog, m_control.get().m_shader);
|
||||||
|
glDetachShader(m_prog, m_evaluation.get().m_shader);
|
||||||
|
m_control.reset();
|
||||||
|
m_evaluation.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExtraParameters() const
|
||||||
|
{
|
||||||
|
glPatchParameteri(GL_PATCH_VERTICES, m_patchSize);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const GLenum BLEND_FACTOR_TABLE[] =
|
GLShareableShader::Token GLDataFactoryImpl::PrepareShaderStage(const char* source, GLenum stage)
|
||||||
{
|
{
|
||||||
GL_ZERO,
|
XXH64_state_t hashState;
|
||||||
GL_ONE,
|
XXH64_reset(&hashState, 0);
|
||||||
GL_SRC_COLOR,
|
XXH64_update(&hashState, source, strlen(source));
|
||||||
GL_ONE_MINUS_SRC_COLOR,
|
uint64_t hash = XXH64_digest(&hashState);
|
||||||
GL_DST_COLOR,
|
|
||||||
GL_ONE_MINUS_DST_COLOR,
|
GLint status;
|
||||||
GL_SRC_ALPHA,
|
auto search = m_sharedShaders.find(hash);
|
||||||
GL_ONE_MINUS_SRC_ALPHA,
|
if (search != m_sharedShaders.end())
|
||||||
GL_DST_ALPHA,
|
{
|
||||||
GL_ONE_MINUS_DST_ALPHA,
|
return search->second->lock();
|
||||||
GL_SRC1_COLOR,
|
}
|
||||||
GL_ONE_MINUS_SRC1_COLOR
|
else
|
||||||
};
|
{
|
||||||
|
GLuint sobj = glCreateShader(stage);
|
||||||
|
if (!sobj)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Fatal, "unable to create shader");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
glShaderSource(sobj, 1, &source, nullptr);
|
||||||
|
glCompileShader(sobj);
|
||||||
|
glGetShaderiv(sobj, GL_COMPILE_STATUS, &status);
|
||||||
|
if (status != GL_TRUE)
|
||||||
|
{
|
||||||
|
GLint logLen;
|
||||||
|
glGetShaderiv(sobj, GL_INFO_LOG_LENGTH, &logLen);
|
||||||
|
std::unique_ptr<char[]> log(new char[logLen]);
|
||||||
|
glGetShaderInfoLog(sobj, logLen, nullptr, log.get());
|
||||||
|
Log.report(logvisor::Fatal, "unable to compile source\n%s\n%s\n", log.get(), source);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it =
|
||||||
|
m_sharedShaders.emplace(std::make_pair(hash,
|
||||||
|
std::make_unique<GLShareableShader>(*this, hash, sobj))).first;
|
||||||
|
return it->second->lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ObjToken<IShaderPipeline> GLDataFactory::Context::newShaderPipeline
|
ObjToken<IShaderPipeline> GLDataFactory::Context::newShaderPipeline
|
||||||
(const char* vertSource, const char* fragSource,
|
(const char* vertSource, const char* fragSource,
|
||||||
|
@ -830,112 +987,43 @@ ObjToken<IShaderPipeline> GLDataFactory::Context::newShaderPipeline
|
||||||
bool alphaWrite, CullMode culling, bool overwriteAlpha)
|
bool alphaWrite, CullMode culling, bool overwriteAlpha)
|
||||||
{
|
{
|
||||||
GLDataFactoryImpl& factory = static_cast<GLDataFactoryImpl&>(m_parent);
|
GLDataFactoryImpl& factory = static_cast<GLDataFactoryImpl&>(m_parent);
|
||||||
ObjToken<IShaderPipeline> retval(new GLShaderPipeline(m_data));
|
ObjToken<IShaderPipeline> retval(new GLShaderPipeline(
|
||||||
|
m_data, texCount, texNames, uniformBlockCount, uniformBlockNames, srcFac, dstFac, prim,
|
||||||
|
depthTest, depthWrite, colorWrite, alphaWrite, culling, overwriteAlpha));
|
||||||
GLShaderPipeline& shader = *retval.cast<GLShaderPipeline>();
|
GLShaderPipeline& shader = *retval.cast<GLShaderPipeline>();
|
||||||
|
|
||||||
XXH64_state_t hashState;
|
shader.m_vert = factory.PrepareShaderStage(vertSource, GL_VERTEX_SHADER);
|
||||||
uint64_t hashes[2];
|
shader.m_frag = factory.PrepareShaderStage(fragSource, GL_FRAGMENT_SHADER);
|
||||||
XXH64_reset(&hashState, 0);
|
|
||||||
XXH64_update(&hashState, vertSource, strlen(vertSource));
|
|
||||||
hashes[0] = XXH64_digest(&hashState);
|
|
||||||
XXH64_reset(&hashState, 0);
|
|
||||||
XXH64_update(&hashState, fragSource, strlen(fragSource));
|
|
||||||
hashes[1] = XXH64_digest(&hashState);
|
|
||||||
|
|
||||||
GLint status;
|
return retval;
|
||||||
auto vertFind = factory.m_sharedShaders.find(hashes[0]);
|
}
|
||||||
if (vertFind != factory.m_sharedShaders.end())
|
|
||||||
{
|
|
||||||
shader.m_vert = vertFind->second->lock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GLuint sobj = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
if (!sobj)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Fatal, "unable to create vert shader");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
glShaderSource(sobj, 1, &vertSource, nullptr);
|
ObjToken<IShaderPipeline> GLDataFactory::Context::newTessellationShaderPipeline
|
||||||
glCompileShader(sobj);
|
(const char* vertSource, const char* fragSource,
|
||||||
glGetShaderiv(sobj, GL_COMPILE_STATUS, &status);
|
const char* controlSource, const char* evaluationSource,
|
||||||
if (status != GL_TRUE)
|
size_t texCount, const char** texNames,
|
||||||
{
|
size_t uniformBlockCount, const char** uniformBlockNames,
|
||||||
GLint logLen;
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
glGetShaderiv(sobj, GL_INFO_LOG_LENGTH, &logLen);
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
std::unique_ptr<char[]> log(new char[logLen]);
|
bool alphaWrite, CullMode culling, bool overwriteAlpha)
|
||||||
glGetShaderInfoLog(sobj, logLen, nullptr, log.get());
|
{
|
||||||
Log.report(logvisor::Fatal, "unable to compile vert source\n%s\n%s\n", log.get(), vertSource);
|
GLDataFactoryImpl& factory = static_cast<GLDataFactoryImpl&>(m_parent);
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it =
|
if (!factory.m_hasTessellation)
|
||||||
factory.m_sharedShaders.emplace(std::make_pair(hashes[0],
|
Log.report(logvisor::Fatal, "Device does not support tessellation shaders");
|
||||||
std::make_unique<GLShareableShader>(factory, hashes[0], sobj))).first;
|
if (patchSize > factory.m_maxPatchSize)
|
||||||
shader.m_vert = it->second->lock();
|
Log.report(logvisor::Fatal, "Device supports %d patch vertices, %d requested",
|
||||||
}
|
int(factory.m_maxPatchSize), int(patchSize));
|
||||||
auto fragFind = factory.m_sharedShaders.find(hashes[1]);
|
|
||||||
if (fragFind != factory.m_sharedShaders.end())
|
|
||||||
{
|
|
||||||
shader.m_frag = fragFind->second->lock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GLuint sobj = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
if (!sobj)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Fatal, "unable to create frag shader");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
glShaderSource(sobj, 1, &fragSource, nullptr);
|
ObjToken<IShaderPipeline> retval(new GLTessellationShaderPipeline(
|
||||||
glCompileShader(sobj);
|
m_data, texCount, texNames, uniformBlockCount, uniformBlockNames, srcFac, dstFac, patchSize,
|
||||||
glGetShaderiv(sobj, GL_COMPILE_STATUS, &status);
|
depthTest, depthWrite, colorWrite, alphaWrite, culling, overwriteAlpha));
|
||||||
if (status != GL_TRUE)
|
GLTessellationShaderPipeline& shader = *retval.cast<GLTessellationShaderPipeline>();
|
||||||
{
|
|
||||||
GLint logLen;
|
|
||||||
glGetShaderiv(sobj, GL_INFO_LOG_LENGTH, &logLen);
|
|
||||||
std::unique_ptr<char[]> log(new char[logLen]);
|
|
||||||
glGetShaderInfoLog(sobj, logLen, nullptr, log.get());
|
|
||||||
Log.report(logvisor::Fatal, "unable to compile frag source\n%s\n%s\n", log.get(), fragSource);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it =
|
shader.m_vert = factory.PrepareShaderStage(vertSource, GL_VERTEX_SHADER);
|
||||||
factory.m_sharedShaders.emplace(std::make_pair(hashes[1],
|
shader.m_frag = factory.PrepareShaderStage(fragSource, GL_FRAGMENT_SHADER);
|
||||||
std::make_unique<GLShareableShader>(factory, hashes[1], sobj))).first;
|
shader.m_control = factory.PrepareShaderStage(controlSource, GL_TESS_CONTROL_SHADER);
|
||||||
shader.m_frag = it->second->lock();
|
shader.m_evaluation = factory.PrepareShaderStage(evaluationSource, GL_TESS_EVALUATION_SHADER);
|
||||||
}
|
|
||||||
|
|
||||||
shader.m_texNames.reserve(texCount);
|
|
||||||
for (int i=0 ; i<texCount ; ++i)
|
|
||||||
shader.m_texNames.emplace_back(texNames[i]);
|
|
||||||
|
|
||||||
shader.m_blockNames.reserve(uniformBlockCount);
|
|
||||||
for (int i=0 ; i<uniformBlockCount ; ++i)
|
|
||||||
shader.m_blockNames.emplace_back(uniformBlockNames[i]);
|
|
||||||
|
|
||||||
if (srcFac == BlendFactor::Subtract || dstFac == BlendFactor::Subtract)
|
|
||||||
{
|
|
||||||
shader.m_sfactor = GL_SRC_ALPHA;
|
|
||||||
shader.m_dfactor = GL_ONE;
|
|
||||||
shader.m_subtractBlend = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shader.m_sfactor = BLEND_FACTOR_TABLE[int(srcFac)];
|
|
||||||
shader.m_dfactor = BLEND_FACTOR_TABLE[int(dstFac)];
|
|
||||||
shader.m_subtractBlend = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.m_depthTest = depthTest;
|
|
||||||
shader.m_depthWrite = depthWrite;
|
|
||||||
shader.m_colorWrite = colorWrite;
|
|
||||||
shader.m_alphaWrite = alphaWrite;
|
|
||||||
shader.m_overwriteAlpha = overwriteAlpha;
|
|
||||||
shader.m_culling = culling;
|
|
||||||
shader.m_drawPrim = PRIMITIVE_TABLE[int(prim)];
|
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,8 +170,18 @@ public:
|
||||||
UpdateGammaLUT(m_gammaLUT.get(), gamma);
|
UpdateGammaLUT(m_gammaLUT.get(), gamma);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CompileVert(std::vector<unsigned int>& out, const char* vertSource, uint64_t srcKey);
|
bool isTessellationSupported(uint32_t& maxPatchSizeOut)
|
||||||
uint64_t CompileFrag(std::vector<unsigned int>& out, const char* fragSource, uint64_t srcKey);
|
{
|
||||||
|
maxPatchSizeOut = 0;
|
||||||
|
if (!m_ctx->m_features.tessellationShader)
|
||||||
|
return false;
|
||||||
|
maxPatchSizeOut = m_ctx->m_gpuProps.limits.maxTessellationPatchSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanShareableShader::Token PrepareShaderStage(const char* source, std::vector<unsigned int>* blobOut,
|
||||||
|
EShLanguage lang);
|
||||||
|
uint64_t Compile(std::vector<unsigned int>& out, const char* source, uint64_t srcKey, EShLanguage lang);
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void ThrowIfFailed(VkResult res)
|
static inline void ThrowIfFailed(VkResult res)
|
||||||
|
@ -540,6 +550,12 @@ void VulkanContext::initDevice()
|
||||||
Log.report(logvisor::Fatal,
|
Log.report(logvisor::Fatal,
|
||||||
"Vulkan device does not support DXT-format textures");
|
"Vulkan device does not support DXT-format textures");
|
||||||
features.textureCompressionBC = VK_TRUE;
|
features.textureCompressionBC = VK_TRUE;
|
||||||
|
VkShaderStageFlagBits tessellationDescriptorBit = VkShaderStageFlagBits(0);
|
||||||
|
if (m_features.tessellationShader)
|
||||||
|
{
|
||||||
|
tessellationDescriptorBit = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||||
|
features.tessellationShader = VK_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t extCount = 0;
|
uint32_t extCount = 0;
|
||||||
vk::EnumerateDeviceExtensionProperties(m_gpus[0], nullptr, &extCount, nullptr);
|
vk::EnumerateDeviceExtensionProperties(m_gpus[0], nullptr, &extCount, nullptr);
|
||||||
|
@ -651,7 +667,8 @@ void VulkanContext::initDevice()
|
||||||
layoutBindings[i].binding = i;
|
layoutBindings[i].binding = i;
|
||||||
layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
layoutBindings[i].descriptorCount = 1;
|
layoutBindings[i].descriptorCount = 1;
|
||||||
layoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
layoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT |
|
||||||
|
tessellationDescriptorBit;
|
||||||
layoutBindings[i].pImmutableSamplers = nullptr;
|
layoutBindings[i].pImmutableSamplers = nullptr;
|
||||||
}
|
}
|
||||||
for (int i=BOO_GLSL_MAX_UNIFORM_COUNT ; i<BOO_GLSL_MAX_UNIFORM_COUNT+BOO_GLSL_MAX_TEXTURE_COUNT ; ++i)
|
for (int i=BOO_GLSL_MAX_UNIFORM_COUNT ; i<BOO_GLSL_MAX_UNIFORM_COUNT+BOO_GLSL_MAX_TEXTURE_COUNT ; ++i)
|
||||||
|
@ -659,7 +676,7 @@ void VulkanContext::initDevice()
|
||||||
layoutBindings[i].binding = i;
|
layoutBindings[i].binding = i;
|
||||||
layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
layoutBindings[i].descriptorCount = 1;
|
layoutBindings[i].descriptorCount = 1;
|
||||||
layoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
layoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | tessellationDescriptorBit;
|
||||||
layoutBindings[i].pImmutableSamplers = nullptr;
|
layoutBindings[i].pImmutableSamplers = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1393,6 +1410,12 @@ static void MakeSampler(VulkanContext* ctx, VkSampler& sampOut, TextureClampMode
|
||||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||||
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
||||||
break;
|
break;
|
||||||
|
case TextureClampMode::ClampToBlack:
|
||||||
|
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||||
|
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||||
|
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||||
|
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
|
||||||
|
break;
|
||||||
case TextureClampMode::ClampToEdge:
|
case TextureClampMode::ClampToEdge:
|
||||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
@ -2144,7 +2167,8 @@ struct VulkanVertexFormat : GraphicsDataNode<IVertexFormat>
|
||||||
static const VkPrimitiveTopology PRIMITIVE_TABLE[] =
|
static const VkPrimitiveTopology PRIMITIVE_TABLE[] =
|
||||||
{
|
{
|
||||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
||||||
|
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
|
||||||
};
|
};
|
||||||
|
|
||||||
static const VkBlendFactor BLEND_FACTOR_TABLE[] =
|
static const VkBlendFactor BLEND_FACTOR_TABLE[] =
|
||||||
|
@ -2165,6 +2189,7 @@ static const VkBlendFactor BLEND_FACTOR_TABLE[] =
|
||||||
|
|
||||||
class VulkanShaderPipeline : public GraphicsDataNode<IShaderPipeline>
|
class VulkanShaderPipeline : public GraphicsDataNode<IShaderPipeline>
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
friend class VulkanDataFactory;
|
friend class VulkanDataFactory;
|
||||||
friend struct VulkanShaderDataBinding;
|
friend struct VulkanShaderDataBinding;
|
||||||
VulkanContext* m_ctx;
|
VulkanContext* m_ctx;
|
||||||
|
@ -2208,6 +2233,9 @@ public:
|
||||||
}
|
}
|
||||||
VulkanShaderPipeline& operator=(const VulkanShaderPipeline&) = delete;
|
VulkanShaderPipeline& operator=(const VulkanShaderPipeline&) = delete;
|
||||||
VulkanShaderPipeline(const VulkanShaderPipeline&) = delete;
|
VulkanShaderPipeline(const VulkanShaderPipeline&) = delete;
|
||||||
|
virtual uint32_t defineExtraStages(VkPipelineShaderStageCreateInfo* stages) const { return 0; }
|
||||||
|
virtual const VkPipelineTessellationStateCreateInfo* getTessellationInfo() const { return nullptr; }
|
||||||
|
virtual void resetExtraStages() const {}
|
||||||
VkPipeline bind(VkRenderPass rPass = 0) const
|
VkPipeline bind(VkRenderPass rPass = 0) const
|
||||||
{
|
{
|
||||||
if (!m_pipeline)
|
if (!m_pipeline)
|
||||||
|
@ -2237,7 +2265,7 @@ public:
|
||||||
dynamicState.pDynamicStates = dynamicStateEnables;
|
dynamicState.pDynamicStates = dynamicStateEnables;
|
||||||
dynamicState.dynamicStateCount = 0;
|
dynamicState.dynamicStateCount = 0;
|
||||||
|
|
||||||
VkPipelineShaderStageCreateInfo stages[2] = {};
|
VkPipelineShaderStageCreateInfo stages[4] = {};
|
||||||
|
|
||||||
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
stages[0].pNext = nullptr;
|
stages[0].pNext = nullptr;
|
||||||
|
@ -2255,6 +2283,8 @@ public:
|
||||||
stages[1].pName = "main";
|
stages[1].pName = "main";
|
||||||
stages[1].pSpecializationInfo = nullptr;
|
stages[1].pSpecializationInfo = nullptr;
|
||||||
|
|
||||||
|
uint32_t extraStages = defineExtraStages(&stages[2]);
|
||||||
|
|
||||||
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
|
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
|
||||||
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||||
assemblyInfo.pNext = nullptr;
|
assemblyInfo.pNext = nullptr;
|
||||||
|
@ -2378,10 +2408,11 @@ public:
|
||||||
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||||
pipelineCreateInfo.pNext = nullptr;
|
pipelineCreateInfo.pNext = nullptr;
|
||||||
pipelineCreateInfo.flags = 0;
|
pipelineCreateInfo.flags = 0;
|
||||||
pipelineCreateInfo.stageCount = 2;
|
pipelineCreateInfo.stageCount = 2 + extraStages;
|
||||||
pipelineCreateInfo.pStages = stages;
|
pipelineCreateInfo.pStages = stages;
|
||||||
pipelineCreateInfo.pVertexInputState = &m_vtxFmt.cast<VulkanVertexFormat>()->m_info;
|
pipelineCreateInfo.pVertexInputState = &m_vtxFmt.cast<VulkanVertexFormat>()->m_info;
|
||||||
pipelineCreateInfo.pInputAssemblyState = &assemblyInfo;
|
pipelineCreateInfo.pInputAssemblyState = &assemblyInfo;
|
||||||
|
pipelineCreateInfo.pTessellationState = getTessellationInfo();
|
||||||
pipelineCreateInfo.pViewportState = &viewportInfo;
|
pipelineCreateInfo.pViewportState = &viewportInfo;
|
||||||
pipelineCreateInfo.pRasterizationState = &rasterizationInfo;
|
pipelineCreateInfo.pRasterizationState = &rasterizationInfo;
|
||||||
pipelineCreateInfo.pMultisampleState = &multisampleInfo;
|
pipelineCreateInfo.pMultisampleState = &multisampleInfo;
|
||||||
|
@ -2396,11 +2427,79 @@ public:
|
||||||
|
|
||||||
m_vert.reset();
|
m_vert.reset();
|
||||||
m_frag.reset();
|
m_frag.reset();
|
||||||
|
resetExtraStages();
|
||||||
}
|
}
|
||||||
return m_pipeline;
|
return m_pipeline;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VulkanTessellationShaderPipeline : public VulkanShaderPipeline
|
||||||
|
{
|
||||||
|
friend class VulkanDataFactory;
|
||||||
|
friend struct VulkanShaderDataBinding;
|
||||||
|
|
||||||
|
mutable VulkanShareableShader::Token m_control;
|
||||||
|
mutable VulkanShareableShader::Token m_evaluation;
|
||||||
|
|
||||||
|
VkPipelineTessellationStateCreateInfo m_tessInfo;
|
||||||
|
|
||||||
|
VulkanTessellationShaderPipeline(const boo::ObjToken<BaseGraphicsData>& parent,
|
||||||
|
VulkanContext* ctx,
|
||||||
|
VulkanShareableShader::Token&& vert,
|
||||||
|
VulkanShareableShader::Token&& frag,
|
||||||
|
VulkanShareableShader::Token&& control,
|
||||||
|
VulkanShareableShader::Token&& evaluation,
|
||||||
|
VkPipelineCache pipelineCache,
|
||||||
|
const boo::ObjToken<IVertexFormat>& vtxFmt,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, bool overwriteAlpha, CullMode culling)
|
||||||
|
: VulkanShaderPipeline(parent, ctx, std::move(vert), std::move(frag), pipelineCache, vtxFmt, srcFac, dstFac,
|
||||||
|
Primitive::Patches, depthTest, depthWrite, colorWrite, alphaWrite, overwriteAlpha, culling),
|
||||||
|
m_control(std::move(control)), m_evaluation(std::move(evaluation))
|
||||||
|
{
|
||||||
|
m_tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||||
|
m_tessInfo.pNext = nullptr;
|
||||||
|
m_tessInfo.flags = 0;
|
||||||
|
m_tessInfo.patchControlPoints = patchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
~VulkanTessellationShaderPipeline() = default;
|
||||||
|
|
||||||
|
uint32_t defineExtraStages(VkPipelineShaderStageCreateInfo* stages) const
|
||||||
|
{
|
||||||
|
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
stages[0].pNext = nullptr;
|
||||||
|
stages[0].flags = 0;
|
||||||
|
stages[0].stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||||
|
stages[0].module = m_control.get().m_shader;
|
||||||
|
stages[0].pName = "main";
|
||||||
|
stages[0].pSpecializationInfo = nullptr;
|
||||||
|
|
||||||
|
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
stages[1].pNext = nullptr;
|
||||||
|
stages[1].flags = 0;
|
||||||
|
stages[1].stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||||
|
stages[1].module = m_evaluation.get().m_shader;
|
||||||
|
stages[1].pName = "main";
|
||||||
|
stages[1].pSpecializationInfo = nullptr;
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkPipelineTessellationStateCreateInfo* getTessellationInfo() const
|
||||||
|
{
|
||||||
|
return &m_tessInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetExtraStages() const
|
||||||
|
{
|
||||||
|
m_control.reset();
|
||||||
|
m_evaluation.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const VkDescriptorBufferInfo* GetBufferGPUResource(const IGraphicsBuffer* buf, int idx)
|
static const VkDescriptorBufferInfo* GetBufferGPUResource(const IGraphicsBuffer* buf, int idx)
|
||||||
{
|
{
|
||||||
if (buf->dynamic())
|
if (buf->dynamic())
|
||||||
|
@ -3474,52 +3573,86 @@ void VulkanTextureD::unmap()
|
||||||
VulkanDataFactoryImpl::VulkanDataFactoryImpl(IGraphicsContext* parent, VulkanContext* ctx)
|
VulkanDataFactoryImpl::VulkanDataFactoryImpl(IGraphicsContext* parent, VulkanContext* ctx)
|
||||||
: m_parent(parent), m_ctx(ctx) {}
|
: m_parent(parent), m_ctx(ctx) {}
|
||||||
|
|
||||||
uint64_t VulkanDataFactoryImpl::CompileVert(std::vector<unsigned int>& out, const char* vertSource, uint64_t srcKey)
|
VulkanShareableShader::Token VulkanDataFactoryImpl::PrepareShaderStage(const char* source,
|
||||||
|
std::vector<unsigned int>* blobOut,
|
||||||
|
EShLanguage lang)
|
||||||
{
|
{
|
||||||
const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
|
uint64_t srcHash = 0;
|
||||||
glslang::TShader vs(EShLangVertex);
|
uint64_t binHash = 0;
|
||||||
vs.setStrings(&vertSource, 1);
|
|
||||||
if (!vs.parse(&glslang::DefaultTBuiltInResource, 110, false, messages))
|
|
||||||
{
|
|
||||||
printf("%s\n", vertSource);
|
|
||||||
Log.report(logvisor::Fatal, "unable to compile vertex shader\n%s", vs.getInfoLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
glslang::TProgram prog;
|
|
||||||
prog.addShader(&vs);
|
|
||||||
if (!prog.link(messages))
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog());
|
|
||||||
}
|
|
||||||
glslang::GlslangToSpv(*prog.getIntermediate(EShLangVertex), out);
|
|
||||||
//spv::Disassemble(std::cerr, out);
|
|
||||||
|
|
||||||
XXH64_state_t hashState;
|
XXH64_state_t hashState;
|
||||||
XXH64_reset(&hashState, 0);
|
XXH64_reset(&hashState, 0);
|
||||||
XXH64_update(&hashState, out.data(), out.size() * sizeof(unsigned int));
|
if (source)
|
||||||
uint64_t binKey = XXH64_digest(&hashState);
|
{
|
||||||
m_sourceToBinary[srcKey] = binKey;
|
XXH64_update(&hashState, source, strlen(source));
|
||||||
return binKey;
|
srcHash = XXH64_digest(&hashState);
|
||||||
|
auto binSearch = m_sourceToBinary.find(srcHash);
|
||||||
|
if (binSearch != m_sourceToBinary.cend())
|
||||||
|
binHash = binSearch->second;
|
||||||
|
}
|
||||||
|
else if (blobOut && blobOut->size())
|
||||||
|
{
|
||||||
|
XXH64_update(&hashState, blobOut->data(), blobOut->size() * sizeof(unsigned int));
|
||||||
|
binHash = XXH64_digest(&hashState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blobOut && blobOut->empty())
|
||||||
|
binHash = Compile(*blobOut, source, srcHash, lang);
|
||||||
|
|
||||||
|
auto search = binHash ? m_sharedShaders.find(binHash) : m_sharedShaders.end();
|
||||||
|
if (search != m_sharedShaders.end())
|
||||||
|
{
|
||||||
|
return search->second->lock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> blob;
|
||||||
|
const std::vector<unsigned int>* useBlob;
|
||||||
|
if (blobOut)
|
||||||
|
{
|
||||||
|
useBlob = blobOut;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
useBlob = &blob;
|
||||||
|
binHash = Compile(blob, source, srcHash, lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo smCreateInfo = {};
|
||||||
|
smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
|
smCreateInfo.pNext = nullptr;
|
||||||
|
smCreateInfo.flags = 0;
|
||||||
|
|
||||||
|
VkShaderModule module;
|
||||||
|
smCreateInfo.codeSize = useBlob->size() * sizeof(unsigned int);
|
||||||
|
smCreateInfo.pCode = useBlob->data();
|
||||||
|
ThrowIfFailed(vk::CreateShaderModule(m_ctx->m_dev, &smCreateInfo, nullptr, &module));
|
||||||
|
|
||||||
|
auto it =
|
||||||
|
m_sharedShaders.emplace(std::make_pair(binHash,
|
||||||
|
std::make_unique<VulkanShareableShader>(*this, srcHash, binHash, module))).first;
|
||||||
|
return it->second->lock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t VulkanDataFactoryImpl::CompileFrag(std::vector<unsigned int>& out, const char* fragSource, uint64_t srcKey)
|
uint64_t VulkanDataFactoryImpl::Compile(std::vector<unsigned int>& out, const char* source,
|
||||||
|
uint64_t srcKey, EShLanguage lang)
|
||||||
{
|
{
|
||||||
const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
|
const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
|
||||||
glslang::TShader fs(EShLangFragment);
|
glslang::TShader shader(lang);
|
||||||
fs.setStrings(&fragSource, 1);
|
shader.setStrings(&source, 1);
|
||||||
if (!fs.parse(&glslang::DefaultTBuiltInResource, 110, false, messages))
|
if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages))
|
||||||
{
|
{
|
||||||
printf("%s\n", fragSource);
|
printf("%s\n", source);
|
||||||
Log.report(logvisor::Fatal, "unable to compile fragment shader\n%s", fs.getInfoLog());
|
Log.report(logvisor::Fatal, "unable to compile shader\n%s", shader.getInfoLog());
|
||||||
}
|
}
|
||||||
|
|
||||||
glslang::TProgram prog;
|
glslang::TProgram prog;
|
||||||
prog.addShader(&fs);
|
prog.addShader(&shader);
|
||||||
if (!prog.link(messages))
|
if (!prog.link(messages))
|
||||||
{
|
{
|
||||||
Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog());
|
Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog());
|
||||||
}
|
}
|
||||||
glslang::GlslangToSpv(*prog.getIntermediate(EShLangFragment), out);
|
glslang::GlslangToSpv(*prog.getIntermediate(lang), out);
|
||||||
//spv::Disassemble(std::cerr, out);
|
//spv::Disassemble(std::cerr, out);
|
||||||
|
|
||||||
XXH64_state_t hashState;
|
XXH64_state_t hashState;
|
||||||
|
@ -3540,111 +3673,8 @@ boo::ObjToken<IShaderPipeline> VulkanDataFactory::Context::newShaderPipeline
|
||||||
{
|
{
|
||||||
VulkanDataFactoryImpl& factory = static_cast<VulkanDataFactoryImpl&>(m_parent);
|
VulkanDataFactoryImpl& factory = static_cast<VulkanDataFactoryImpl&>(m_parent);
|
||||||
|
|
||||||
XXH64_state_t hashState;
|
VulkanShareableShader::Token vertShader = factory.PrepareShaderStage(vertSource, vertBlobOut, EShLangVertex);
|
||||||
uint64_t srcHashes[2] = {};
|
VulkanShareableShader::Token fragShader = factory.PrepareShaderStage(fragSource, fragBlobOut, EShLangFragment);
|
||||||
uint64_t binHashes[2] = {};
|
|
||||||
XXH64_reset(&hashState, 0);
|
|
||||||
if (vertSource)
|
|
||||||
{
|
|
||||||
XXH64_update(&hashState, vertSource, strlen(vertSource));
|
|
||||||
srcHashes[0] = XXH64_digest(&hashState);
|
|
||||||
auto binSearch = factory.m_sourceToBinary.find(srcHashes[0]);
|
|
||||||
if (binSearch != factory.m_sourceToBinary.cend())
|
|
||||||
binHashes[0] = binSearch->second;
|
|
||||||
}
|
|
||||||
else if (vertBlobOut && vertBlobOut->size())
|
|
||||||
{
|
|
||||||
XXH64_update(&hashState, vertBlobOut->data(), vertBlobOut->size() * sizeof(unsigned int));
|
|
||||||
binHashes[0] = XXH64_digest(&hashState);
|
|
||||||
}
|
|
||||||
XXH64_reset(&hashState, 0);
|
|
||||||
if (fragSource)
|
|
||||||
{
|
|
||||||
XXH64_update(&hashState, fragSource, strlen(fragSource));
|
|
||||||
srcHashes[1] = XXH64_digest(&hashState);
|
|
||||||
auto binSearch = factory.m_sourceToBinary.find(srcHashes[1]);
|
|
||||||
if (binSearch != factory.m_sourceToBinary.cend())
|
|
||||||
binHashes[1] = binSearch->second;
|
|
||||||
}
|
|
||||||
else if (fragBlobOut && fragBlobOut->size())
|
|
||||||
{
|
|
||||||
XXH64_update(&hashState, fragBlobOut->data(), fragBlobOut->size() * sizeof(unsigned int));
|
|
||||||
binHashes[1] = XXH64_digest(&hashState);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vertBlobOut && vertBlobOut->empty())
|
|
||||||
binHashes[0] = factory.CompileVert(*vertBlobOut, vertSource, srcHashes[0]);
|
|
||||||
|
|
||||||
if (fragBlobOut && fragBlobOut->empty())
|
|
||||||
binHashes[1] = factory.CompileFrag(*fragBlobOut, fragSource, srcHashes[1]);
|
|
||||||
|
|
||||||
VkShaderModuleCreateInfo smCreateInfo = {};
|
|
||||||
smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
||||||
smCreateInfo.pNext = nullptr;
|
|
||||||
smCreateInfo.flags = 0;
|
|
||||||
|
|
||||||
VulkanShareableShader::Token vertShader;
|
|
||||||
VulkanShareableShader::Token fragShader;
|
|
||||||
auto vertFind = binHashes[0] ? factory.m_sharedShaders.find(binHashes[0]) :
|
|
||||||
factory.m_sharedShaders.end();
|
|
||||||
if (vertFind != factory.m_sharedShaders.end())
|
|
||||||
{
|
|
||||||
vertShader = vertFind->second->lock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<unsigned int> vertBlob;
|
|
||||||
const std::vector<unsigned int>* useVertBlob;
|
|
||||||
if (vertBlobOut)
|
|
||||||
{
|
|
||||||
useVertBlob = vertBlobOut;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
useVertBlob = &vertBlob;
|
|
||||||
binHashes[0] = factory.CompileVert(vertBlob, vertSource, srcHashes[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkShaderModule vertModule;
|
|
||||||
smCreateInfo.codeSize = useVertBlob->size() * sizeof(unsigned int);
|
|
||||||
smCreateInfo.pCode = useVertBlob->data();
|
|
||||||
ThrowIfFailed(vk::CreateShaderModule(factory.m_ctx->m_dev, &smCreateInfo, nullptr, &vertModule));
|
|
||||||
|
|
||||||
auto it =
|
|
||||||
factory.m_sharedShaders.emplace(std::make_pair(binHashes[0],
|
|
||||||
std::make_unique<VulkanShareableShader>(factory, srcHashes[0], binHashes[0], vertModule))).first;
|
|
||||||
vertShader = it->second->lock();
|
|
||||||
}
|
|
||||||
auto fragFind = binHashes[1] ? factory.m_sharedShaders.find(binHashes[1]) :
|
|
||||||
factory.m_sharedShaders.end();
|
|
||||||
if (fragFind != factory.m_sharedShaders.end())
|
|
||||||
{
|
|
||||||
fragShader = fragFind->second->lock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<unsigned int> fragBlob;
|
|
||||||
const std::vector<unsigned int>* useFragBlob;
|
|
||||||
if (fragBlobOut)
|
|
||||||
{
|
|
||||||
useFragBlob = fragBlobOut;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
useFragBlob = &fragBlob;
|
|
||||||
binHashes[1] = factory.CompileFrag(fragBlob, fragSource, srcHashes[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkShaderModule fragModule;
|
|
||||||
smCreateInfo.codeSize = useFragBlob->size() * sizeof(unsigned int);
|
|
||||||
smCreateInfo.pCode = useFragBlob->data();
|
|
||||||
ThrowIfFailed(vk::CreateShaderModule(factory.m_ctx->m_dev, &smCreateInfo, nullptr, &fragModule));
|
|
||||||
|
|
||||||
auto it =
|
|
||||||
factory.m_sharedShaders.emplace(std::make_pair(binHashes[1],
|
|
||||||
std::make_unique<VulkanShareableShader>(factory, srcHashes[1], binHashes[1], fragModule))).first;
|
|
||||||
fragShader = it->second->lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
||||||
if (pipelineBlob)
|
if (pipelineBlob)
|
||||||
|
@ -3681,6 +3711,65 @@ boo::ObjToken<IShaderPipeline> VulkanDataFactory::Context::newShaderPipeline
|
||||||
return {retval};
|
return {retval};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boo::ObjToken<IShaderPipeline> VulkanDataFactory::Context::newTessellationShaderPipeline
|
||||||
|
(const char* vertSource, const char* fragSource, const char* controlSource, const char* evaluationSource,
|
||||||
|
std::vector<unsigned int>* vertBlobOut, std::vector<unsigned int>* fragBlobOut,
|
||||||
|
std::vector<unsigned int>* controlBlobOut, std::vector<unsigned int>* evaluationBlobOut,
|
||||||
|
std::vector<unsigned char>* pipelineBlob, const boo::ObjToken<IVertexFormat>& vtxFmt,
|
||||||
|
BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize,
|
||||||
|
ZTest depthTest, bool depthWrite, bool colorWrite,
|
||||||
|
bool alphaWrite, CullMode culling, bool overwriteAlpha)
|
||||||
|
{
|
||||||
|
VulkanDataFactoryImpl& factory = static_cast<VulkanDataFactoryImpl&>(m_parent);
|
||||||
|
|
||||||
|
if (!factory.m_ctx->m_features.tessellationShader)
|
||||||
|
Log.report(logvisor::Fatal, "Device does not support tessellation shaders");
|
||||||
|
if (patchSize > factory.m_ctx->m_gpuProps.limits.maxTessellationPatchSize)
|
||||||
|
Log.report(logvisor::Fatal, "Device supports %d patch vertices, %d requested",
|
||||||
|
int(factory.m_ctx->m_gpuProps.limits.maxTessellationPatchSize), int(patchSize));
|
||||||
|
|
||||||
|
VulkanShareableShader::Token vertShader = factory.PrepareShaderStage(vertSource, vertBlobOut, EShLangVertex);
|
||||||
|
VulkanShareableShader::Token fragShader = factory.PrepareShaderStage(fragSource, fragBlobOut, EShLangFragment);
|
||||||
|
VulkanShareableShader::Token controlShader = factory.PrepareShaderStage(controlSource, controlBlobOut, EShLangTessControl);
|
||||||
|
VulkanShareableShader::Token evaluationShader = factory.PrepareShaderStage(evaluationSource, evaluationBlobOut, EShLangTessEvaluation);
|
||||||
|
|
||||||
|
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
||||||
|
if (pipelineBlob)
|
||||||
|
{
|
||||||
|
VkPipelineCacheCreateInfo cacheDataInfo = {};
|
||||||
|
cacheDataInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||||
|
cacheDataInfo.pNext = nullptr;
|
||||||
|
|
||||||
|
cacheDataInfo.initialDataSize = pipelineBlob->size();
|
||||||
|
if (cacheDataInfo.initialDataSize)
|
||||||
|
cacheDataInfo.pInitialData = pipelineBlob->data();
|
||||||
|
|
||||||
|
ThrowIfFailed(vk::CreatePipelineCache(factory.m_ctx->m_dev, &cacheDataInfo, nullptr, &pipelineCache));
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanShaderPipeline* retval =
|
||||||
|
new VulkanTessellationShaderPipeline(m_data, factory.m_ctx, std::move(vertShader),
|
||||||
|
std::move(fragShader), std::move(controlShader),
|
||||||
|
std::move(evaluationShader), pipelineCache, vtxFmt, srcFac,
|
||||||
|
dstFac, patchSize, depthTest, depthWrite, colorWrite,
|
||||||
|
alphaWrite, overwriteAlpha, culling);
|
||||||
|
|
||||||
|
if (pipelineBlob && pipelineBlob->empty())
|
||||||
|
{
|
||||||
|
size_t cacheSz = 0;
|
||||||
|
ThrowIfFailed(vk::GetPipelineCacheData(factory.m_ctx->m_dev, pipelineCache, &cacheSz, nullptr));
|
||||||
|
if (cacheSz)
|
||||||
|
{
|
||||||
|
pipelineBlob->resize(cacheSz);
|
||||||
|
ThrowIfFailed(vk::GetPipelineCacheData(factory.m_ctx->m_dev, pipelineCache,
|
||||||
|
&cacheSz, pipelineBlob->data()));
|
||||||
|
pipelineBlob->resize(cacheSz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {retval};
|
||||||
|
}
|
||||||
|
|
||||||
VulkanDataFactory::Context::Context(VulkanDataFactory& parent __BooTraceArgs)
|
VulkanDataFactory::Context::Context(VulkanDataFactory& parent __BooTraceArgs)
|
||||||
: m_parent(parent), m_data(new VulkanData(static_cast<VulkanDataFactoryImpl&>(parent) __BooTraceArgsUse)) {}
|
: m_parent(parent), m_data(new VulkanData(static_cast<VulkanDataFactoryImpl&>(parent) __BooTraceArgsUse)) {}
|
||||||
VulkanDataFactory::Context::~Context() {}
|
VulkanDataFactory::Context::~Context() {}
|
||||||
|
|
Loading…
Reference in New Issue