Commit e6664f06 by Cooper Partin Committed by Geoff Lang

Added PointSprites Support for renderers that do not support Geometry Shaders

Change-Id: Iae9ac5f8fbba68dba5e49ccda7bb7eebb05c8e9a Reviewed-on: https://chromium-review.googlesource.com/240450Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 31b5fc62
...@@ -140,8 +140,8 @@ OutputHLSL::OutputHLSL(TParseContext &context, TranslatorHLSL *parentTranslator) ...@@ -140,8 +140,8 @@ OutputHLSL::OutputHLSL(TParseContext &context, TranslatorHLSL *parentTranslator)
} }
else else
{ {
// Reserve registers for dx_DepthRange and dx_ViewAdjust // Reserve registers for dx_DepthRange, dx_ViewAdjust and dx_ViewCoords
mUniformHLSL->reserveUniformRegisters(2); mUniformHLSL->reserveUniformRegisters(3);
} }
} }
...@@ -510,10 +510,11 @@ void OutputHLSL::header(const BuiltInFunctionEmulatorHLSL *builtInFunctionEmulat ...@@ -510,10 +510,11 @@ void OutputHLSL::header(const BuiltInFunctionEmulatorHLSL *builtInFunctionEmulat
out << " float3 dx_DepthRange : packoffset(c0);\n"; out << " float3 dx_DepthRange : packoffset(c0);\n";
} }
// dx_ViewAdjust will only be used in Feature Level 9 shaders. // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 shaders.
// However, we declare it for all shaders (including Feature Level 10+). // However, we declare it for all shaders (including Feature Level 10+).
// The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused. // The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused.
out << " float4 dx_ViewAdjust : packoffset(c1);\n"; out << " float4 dx_ViewAdjust : packoffset(c1);\n";
out << " float2 dx_ViewCoords : packoffset(c2);\n";
out << "};\n" out << "};\n"
"\n"; "\n";
...@@ -525,7 +526,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulatorHLSL *builtInFunctionEmulat ...@@ -525,7 +526,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulatorHLSL *builtInFunctionEmulat
out << "uniform float3 dx_DepthRange : register(c0);\n"; out << "uniform float3 dx_DepthRange : register(c0);\n";
} }
out << "uniform float4 dx_ViewAdjust : register(c1);\n" out << "uniform float4 dx_ViewAdjust : register(c1);\n";
out << "uniform float2 dx_ViewCoords : register(c2);\n"
"\n"; "\n";
} }
......
...@@ -28,7 +28,8 @@ struct Workarounds ...@@ -28,7 +28,8 @@ struct Workarounds
Workarounds() Workarounds()
: mrtPerfWorkaround(false), : mrtPerfWorkaround(false),
setDataFasterThanImageUpload(false), setDataFasterThanImageUpload(false),
zeroMaxLodWorkaround(false) zeroMaxLodWorkaround(false),
useInstancedPointSpriteEmulation(false)
{} {}
bool mrtPerfWorkaround; bool mrtPerfWorkaround;
...@@ -40,6 +41,12 @@ struct Workarounds ...@@ -40,6 +41,12 @@ struct Workarounds
// This causes problems when (for example) an application creates a mipmapped texture2D, but sets GL_TEXTURE_MIN_FILTER to GL_NEAREST (i.e disables mipmaps). // This causes problems when (for example) an application creates a mipmapped texture2D, but sets GL_TEXTURE_MIN_FILTER to GL_NEAREST (i.e disables mipmaps).
// To work around this, D3D11 FL9_3 has to create two copies of the texture. The textures' level zeros are identical, but only one texture has mips. // To work around this, D3D11 FL9_3 has to create two copies of the texture. The textures' level zeros are identical, but only one texture has mips.
bool zeroMaxLodWorkaround; bool zeroMaxLodWorkaround;
// Some renderers do not support Geometry Shaders so the Geometry Shader-based
// PointSprite emulation will not work.
// To work around this, D3D11 FL9_3 has to use a different pointsprite
// emulation that is implemented using instanced quads.
bool useInstancedPointSpriteEmulation;
}; };
} }
......
...@@ -420,6 +420,22 @@ std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &s ...@@ -420,6 +420,22 @@ std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &s
} }
} }
// If gl_PointSize is used in the shader then pointsprites rendering is expected.
// If the renderer does not support Geometry shaders then Instanced PointSprite emulation
// may must be used.
bool usesPointSize = sourceShader.find("GL_USES_POINT_SIZE") != std::string::npos;
bool useInstancedPointSpriteEmulation = usesPointSize && mRenderer->getWorkarounds().useInstancedPointSpriteEmulation;
// Instanced PointSprite emulation requires additional entries in the
// VS_INPUT structure to support the vertices that make up the quad vertices.
// These values must be in sync with the cooresponding values added during inputlayout creation
// in InputLayoutCache::applyVertexBuffers().
if (useInstancedPointSpriteEmulation)
{
structHLSL += " float3 spriteVertexPos : SPRITEPOSITION0;\n";
structHLSL += " float2 spriteTexCoord : SPRITETEXCOORD0;\n";
}
std::string replacementHLSL = "struct VS_INPUT\n" std::string replacementHLSL = "struct VS_INPUT\n"
"{\n" + "{\n" +
structHLSL + structHLSL +
...@@ -694,6 +710,7 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog, ...@@ -694,6 +710,7 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog,
bool usesFragCoord = fragmentShader->mUsesFragCoord; bool usesFragCoord = fragmentShader->mUsesFragCoord;
bool usesPointCoord = fragmentShader->mUsesPointCoord; bool usesPointCoord = fragmentShader->mUsesPointCoord;
bool usesPointSize = vertexShader->mUsesPointSize; bool usesPointSize = vertexShader->mUsesPointSize;
bool useInstancedPointSpriteEmulation = usesPointSize && mRenderer->getWorkarounds().useInstancedPointSpriteEmulation;
if (usesFragColor && usesFragData) if (usesFragColor && usesFragData)
{ {
...@@ -720,12 +737,27 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog, ...@@ -720,12 +737,27 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog,
} }
const std::string &varyingHLSL = generateVaryingHLSL(vertexShader); const std::string &varyingHLSL = generateVaryingHLSL(vertexShader);
// Instanced PointSprite emulation requires that gl_PointCoord is present in the vertex shader VS_OUTPUT
// structure to ensure compatibility with the generated PS_INPUT of the pixel shader.
// GeometryShader PointSprite emulation does not require this additional entry because the
// GS_OUTPUT of the Geometry shader contains the pointCoord value and already matches the PS_INPUT of the
// generated pixel shader.
const SemanticInfo &vertexSemantics = getSemanticInfo(registers, usesFragCoord, const SemanticInfo &vertexSemantics = getSemanticInfo(registers, usesFragCoord,
false, usesPointSize, false); (useInstancedPointSpriteEmulation && usesPointCoord),
usesPointSize, false);
storeUserLinkedVaryings(vertexShader, linkedVaryings); storeUserLinkedVaryings(vertexShader, linkedVaryings);
storeBuiltinLinkedVaryings(vertexSemantics, linkedVaryings); storeBuiltinLinkedVaryings(vertexSemantics, linkedVaryings);
// Instanced PointSprite emulation requires additional entries originally generated in the
// GeometryShader HLSL. These include pointsize clamp values.
if (useInstancedPointSpriteEmulation)
{
vertexHLSL += "static float minPointSize = " + Str((int)mRenderer->getRendererCaps().minAliasedPointSize) + ".0f;\n"
"static float maxPointSize = " + Str((int)mRenderer->getRendererCaps().maxAliasedPointSize) + ".0f;\n";
}
// Add stub string to be replaced when shader is dynamically defined by its layout // Add stub string to be replaced when shader is dynamically defined by its layout
vertexHLSL += "\n" + VERTEX_ATTRIBUTE_STUB_STRING + "\n" vertexHLSL += "\n" + VERTEX_ATTRIBUTE_STUB_STRING + "\n"
"struct VS_OUTPUT\n" + generateVaryingLinkHLSL(vertexSemantics, varyingHLSL) + "\n" "struct VS_OUTPUT\n" + generateVaryingLinkHLSL(vertexSemantics, varyingHLSL) + "\n"
...@@ -802,6 +834,22 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog, ...@@ -802,6 +834,22 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog,
} }
} }
// Instanced PointSprite emulation requires additional entries to calculate
// the final output vertex positions of the quad that represents each sprite.
if (useInstancedPointSpriteEmulation)
{
vertexHLSL += "\n"
" gl_PointSize = clamp(gl_PointSize, minPointSize, maxPointSize);\n"
" output.dx_Position.xyz += float3(input.spriteVertexPos.x * gl_PointSize / (dx_ViewCoords.x*2), input.spriteVertexPos.y * gl_PointSize / (dx_ViewCoords.y*2), input.spriteVertexPos.z) * output.dx_Position.w;\n"
" output.gl_PointSize = gl_PointSize;\n";
if (usesPointCoord)
{
vertexHLSL += "\n"
" output.gl_PointCoord = input.spriteTexCoord;\n";
}
}
vertexHLSL += "\n" vertexHLSL += "\n"
" return output;\n" " return output;\n"
"}\n"; "}\n";
......
...@@ -213,7 +213,12 @@ bool ProgramD3D::usesPointSpriteEmulation() const ...@@ -213,7 +213,12 @@ bool ProgramD3D::usesPointSpriteEmulation() const
bool ProgramD3D::usesGeometryShader() const bool ProgramD3D::usesGeometryShader() const
{ {
return usesPointSpriteEmulation(); return usesPointSpriteEmulation() && !usesInstancedPointSpriteEmulation();
}
bool ProgramD3D::usesInstancedPointSpriteEmulation() const
{
return mRenderer->getWorkarounds().useInstancedPointSpriteEmulation;
} }
GLint ProgramD3D::getSamplerMapping(gl::SamplerType type, unsigned int samplerIndex, const gl::Caps &caps) const GLint ProgramD3D::getSamplerMapping(gl::SamplerType type, unsigned int samplerIndex, const gl::Caps &caps) const
......
...@@ -58,6 +58,7 @@ class ProgramD3D : public ProgramImpl ...@@ -58,6 +58,7 @@ class ProgramD3D : public ProgramImpl
bool usesPointSize() const { return mUsesPointSize; } bool usesPointSize() const { return mUsesPointSize; }
bool usesPointSpriteEmulation() const; bool usesPointSpriteEmulation() const;
bool usesGeometryShader() const; bool usesGeometryShader() const;
bool usesInstancedPointSpriteEmulation() const;
GLenum getBinaryFormat() { return GL_PROGRAM_BINARY_ANGLE; } GLenum getBinaryFormat() { return GL_PROGRAM_BINARY_ANGLE; }
LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream); LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream);
......
...@@ -76,7 +76,7 @@ gl::Error RendererD3D::drawElements(const gl::Data &data, ...@@ -76,7 +76,7 @@ gl::Error RendererD3D::drawElements(const gl::Data &data,
return error; return error;
} }
if (!applyPrimitiveType(mode, count)) if (!applyPrimitiveType(mode, count, program->usesPointSize()))
{ {
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -159,7 +159,7 @@ gl::Error RendererD3D::drawArrays(const gl::Data &data, ...@@ -159,7 +159,7 @@ gl::Error RendererD3D::drawArrays(const gl::Data &data,
return error; return error;
} }
if (!applyPrimitiveType(mode, count)) if (!applyPrimitiveType(mode, count, program->usesPointSize()))
{ {
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -204,7 +204,7 @@ gl::Error RendererD3D::drawArrays(const gl::Data &data, ...@@ -204,7 +204,7 @@ gl::Error RendererD3D::drawArrays(const gl::Data &data,
if (!skipDraw(data, mode)) if (!skipDraw(data, mode))
{ {
error = drawArrays(mode, count, instances, transformFeedbackActive); error = drawArrays(mode, count, instances, transformFeedbackActive, program->usesPointSize());
if (error.isError()) if (error.isError())
{ {
return error; return error;
......
...@@ -92,7 +92,7 @@ class RendererD3D : public Renderer ...@@ -92,7 +92,7 @@ class RendererD3D : public Renderer
virtual gl::Error applyShaders(gl::Program *program, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer, virtual gl::Error applyShaders(gl::Program *program, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer,
bool rasterizerDiscard, bool transformFeedbackActive) = 0; bool rasterizerDiscard, bool transformFeedbackActive) = 0;
virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray) = 0; virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray) = 0;
virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount) = 0; virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize) = 0;
virtual gl::Error applyVertexBuffer(const gl::State &state, GLint first, GLsizei count, GLsizei instances) = 0; virtual gl::Error applyVertexBuffer(const gl::State &state, GLint first, GLsizei count, GLsizei instances) = 0;
virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo) = 0; virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo) = 0;
virtual void applyTransformFeedbackBuffers(const gl::State& state) = 0; virtual void applyTransformFeedbackBuffers(const gl::State& state) = 0;
...@@ -156,7 +156,7 @@ class RendererD3D : public Renderer ...@@ -156,7 +156,7 @@ class RendererD3D : public Renderer
gl::Error getScratchMemoryBuffer(size_t requestedSize, MemoryBuffer **bufferOut); gl::Error getScratchMemoryBuffer(size_t requestedSize, MemoryBuffer **bufferOut);
protected: protected:
virtual gl::Error drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive) = 0; virtual gl::Error drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive, bool usesPointSize) = 0;
virtual gl::Error drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, virtual gl::Error drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices,
gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances) = 0; gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances) = 0;
...@@ -201,6 +201,7 @@ struct dx_VertexConstants ...@@ -201,6 +201,7 @@ struct dx_VertexConstants
{ {
float depthRange[4]; float depthRange[4];
float viewAdjust[4]; float viewAdjust[4];
float viewCoords[4];
}; };
struct dx_PixelConstants struct dx_PixelConstants
......
...@@ -51,6 +51,8 @@ InputLayoutCache::InputLayoutCache() : mInputLayoutMap(kMaxInputLayouts, hashInp ...@@ -51,6 +51,8 @@ InputLayoutCache::InputLayoutCache() : mInputLayoutMap(kMaxInputLayouts, hashInp
mCurrentVertexStrides[i] = -1; mCurrentVertexStrides[i] = -1;
mCurrentVertexOffsets[i] = -1; mCurrentVertexOffsets[i] = -1;
} }
mPointSpriteVertexBuffer = NULL;
mPointSpriteIndexBuffer = NULL;
} }
InputLayoutCache::~InputLayoutCache() InputLayoutCache::~InputLayoutCache()
...@@ -73,6 +75,8 @@ void InputLayoutCache::clear() ...@@ -73,6 +75,8 @@ void InputLayoutCache::clear()
SafeRelease(i->second.inputLayout); SafeRelease(i->second.inputLayout);
} }
mInputLayoutMap.clear(); mInputLayoutMap.clear();
SafeRelease(mPointSpriteVertexBuffer);
SafeRelease(mPointSpriteIndexBuffer);
markDirty(); markDirty();
} }
...@@ -94,6 +98,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -94,6 +98,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
int sortedSemanticIndices[gl::MAX_VERTEX_ATTRIBS]; int sortedSemanticIndices[gl::MAX_VERTEX_ATTRIBS];
programD3D->sortAttributesByLayout(attributes, sortedSemanticIndices); programD3D->sortAttributesByLayout(attributes, sortedSemanticIndices);
bool usesInstancedPointSpriteEmulation = programD3D->usesPointSize() && programD3D->usesInstancedPointSpriteEmulation();
if (!mDevice || !mDeviceContext) if (!mDevice || !mDeviceContext)
{ {
...@@ -106,12 +111,15 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -106,12 +111,15 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
unsigned int firstIndexedElement = gl::MAX_VERTEX_ATTRIBS; unsigned int firstIndexedElement = gl::MAX_VERTEX_ATTRIBS;
unsigned int firstInstancedElement = gl::MAX_VERTEX_ATTRIBS; unsigned int firstInstancedElement = gl::MAX_VERTEX_ATTRIBS;
unsigned int nextAvailableInputSlot = 0;
for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
{ {
if (attributes[i].active) if (attributes[i].active)
{ {
D3D11_INPUT_CLASSIFICATION inputClass = attributes[i].divisor > 0 ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; D3D11_INPUT_CLASSIFICATION inputClass = attributes[i].divisor > 0 ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
// If instanced pointsprite emulation is being used, the inputClass is required to be configured as per instance data
inputClass = usesInstancedPointSpriteEmulation ? D3D11_INPUT_PER_INSTANCE_DATA : inputClass;
gl::VertexFormat vertexFormat(*attributes[i].attribute, attributes[i].currentValueType); gl::VertexFormat vertexFormat(*attributes[i].attribute, attributes[i].currentValueType);
const d3d11::VertexFormat &vertexFormatInfo = d3d11::GetVertexFormatInfo(vertexFormat, mFeatureLevel); const d3d11::VertexFormat &vertexFormatInfo = d3d11::GetVertexFormatInfo(vertexFormat, mFeatureLevel);
...@@ -127,7 +135,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -127,7 +135,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
ilKey.elements[ilKey.elementCount].desc.InputSlot = i; ilKey.elements[ilKey.elementCount].desc.InputSlot = i;
ilKey.elements[ilKey.elementCount].desc.AlignedByteOffset = 0; ilKey.elements[ilKey.elementCount].desc.AlignedByteOffset = 0;
ilKey.elements[ilKey.elementCount].desc.InputSlotClass = inputClass; ilKey.elements[ilKey.elementCount].desc.InputSlotClass = inputClass;
ilKey.elements[ilKey.elementCount].desc.InstanceDataStepRate = attributes[i].divisor; ilKey.elements[ilKey.elementCount].desc.InstanceDataStepRate = usesInstancedPointSpriteEmulation ? 1 : attributes[i].divisor;
if (inputClass == D3D11_INPUT_PER_VERTEX_DATA && firstIndexedElement == gl::MAX_VERTEX_ATTRIBS) if (inputClass == D3D11_INPUT_PER_VERTEX_DATA && firstIndexedElement == gl::MAX_VERTEX_ATTRIBS)
{ {
...@@ -139,9 +147,43 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -139,9 +147,43 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
} }
ilKey.elementCount++; ilKey.elementCount++;
nextAvailableInputSlot = i + 1;
} }
} }
// Instanced PointSprite emulation requires additional entries in the
// inputlayout to support the vertices that make up the pointsprite quad.
if (usesInstancedPointSpriteEmulation)
{
ilKey.elements[ilKey.elementCount].desc.SemanticName = "SPRITEPOSITION";
ilKey.elements[ilKey.elementCount].desc.SemanticIndex = 0;
ilKey.elements[ilKey.elementCount].desc.Format = DXGI_FORMAT_R32G32B32_FLOAT;
ilKey.elements[ilKey.elementCount].desc.InputSlot = nextAvailableInputSlot;
ilKey.elements[ilKey.elementCount].desc.AlignedByteOffset = 0;
ilKey.elements[ilKey.elementCount].desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
ilKey.elements[ilKey.elementCount].desc.InstanceDataStepRate = 0;
// The new elements are D3D11_INPUT_PER_VERTEX_DATA data so the indexed element
// tracking must be applied. This ensures that the instancing specific
// buffer swapping logic continues to work.
if (firstIndexedElement == gl::MAX_VERTEX_ATTRIBS)
{
firstIndexedElement = ilKey.elementCount;
}
ilKey.elementCount++;
ilKey.elements[ilKey.elementCount].desc.SemanticName = "SPRITETEXCOORD";
ilKey.elements[ilKey.elementCount].desc.SemanticIndex = 0;
ilKey.elements[ilKey.elementCount].desc.Format = DXGI_FORMAT_R32G32_FLOAT;
ilKey.elements[ilKey.elementCount].desc.InputSlot = nextAvailableInputSlot;
ilKey.elements[ilKey.elementCount].desc.AlignedByteOffset = sizeof(float) * 3;
ilKey.elements[ilKey.elementCount].desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
ilKey.elements[ilKey.elementCount].desc.InstanceDataStepRate = 0;
ilKey.elementCount++;
}
// On 9_3, we must ensure that slot 0 contains non-instanced data. // On 9_3, we must ensure that slot 0 contains non-instanced data.
// If slot 0 currently contains instanced data then we swap it with a non-instanced element. // If slot 0 currently contains instanced data then we swap it with a non-instanced element.
// Note that instancing is only available on 9_3 via ANGLE_instanced_arrays, since 9_3 doesn't support OpenGL ES 3.0. // Note that instancing is only available on 9_3 via ANGLE_instanced_arrays, since 9_3 doesn't support OpenGL ES 3.0.
...@@ -153,6 +195,13 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -153,6 +195,13 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
{ {
ilKey.elements[firstInstancedElement].desc.InputSlot = ilKey.elements[firstIndexedElement].desc.InputSlot; ilKey.elements[firstInstancedElement].desc.InputSlot = ilKey.elements[firstIndexedElement].desc.InputSlot;
ilKey.elements[firstIndexedElement].desc.InputSlot = 0; ilKey.elements[firstIndexedElement].desc.InputSlot = 0;
// Instanced PointSprite emulation uses multiple layout entries across a single vertex buffer.
// If an index swap is performed, we need to ensure that all elements get the proper InputSlot.
if (usesInstancedPointSpriteEmulation)
{
ilKey.elements[firstIndexedElement + 1].desc.InputSlot = 0;
}
} }
ID3D11InputLayout *inputLayout = NULL; ID3D11InputLayout *inputLayout = NULL;
...@@ -223,6 +272,8 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -223,6 +272,8 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
bool dirtyBuffers = false; bool dirtyBuffers = false;
size_t minDiff = gl::MAX_VERTEX_ATTRIBS; size_t minDiff = gl::MAX_VERTEX_ATTRIBS;
size_t maxDiff = 0; size_t maxDiff = 0;
unsigned int nextAvailableIndex = 0;
for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
{ {
ID3D11Buffer *buffer = NULL; ID3D11Buffer *buffer = NULL;
...@@ -249,9 +300,93 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl ...@@ -249,9 +300,93 @@ gl::Error InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl
mCurrentBuffers[i] = buffer; mCurrentBuffers[i] = buffer;
mCurrentVertexStrides[i] = vertexStride; mCurrentVertexStrides[i] = vertexStride;
mCurrentVertexOffsets[i] = vertexOffset; mCurrentVertexOffsets[i] = vertexOffset;
// If a non null ID3D11Buffer is being assigned to mCurrentBuffers,
// then the next available index needs to be tracked to ensure
// that any instanced pointsprite emulation buffers will be properly packed.
if (buffer)
{
nextAvailableIndex = i + 1;
}
} }
} }
// Instanced PointSprite emulation requires two additional ID3D11Buffers.
// A vertex buffer needs to be created and added to the list of current buffers,
// strides and offsets collections. This buffer contains the vertices for a single
// PointSprite quad.
// An index buffer also needs to be created and applied because rendering instanced
// data on D3D11 FL9_3 requires DrawIndexedInstanced() to be used.
if (usesInstancedPointSpriteEmulation)
{
HRESULT result = S_OK;
const UINT pointSpriteVertexStride = sizeof(float) * 5;
if (!mPointSpriteVertexBuffer)
{
static const float pointSpriteVertices[] =
{
// Position // TexCoord
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
};
D3D11_SUBRESOURCE_DATA vertexBufferData = { pointSpriteVertices, 0, 0 };
D3D11_BUFFER_DESC vertexBufferDesc;
vertexBufferDesc.ByteWidth = sizeof(pointSpriteVertices);
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
result = mDevice->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &mPointSpriteVertexBuffer);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to create instanced pointsprite emulation vertex buffer, HRESULT: 0x%08x", result);
}
}
mCurrentBuffers[nextAvailableIndex] = mPointSpriteVertexBuffer;
mCurrentVertexStrides[nextAvailableIndex] = pointSpriteVertexStride;
mCurrentVertexOffsets[nextAvailableIndex] = 0;
if (!mPointSpriteIndexBuffer)
{
// Create an index buffer and set it for pointsprite rendering
static const unsigned short pointSpriteIndices[] =
{
0, 1, 2, 3, 4, 5,
};
D3D11_SUBRESOURCE_DATA indexBufferData = { pointSpriteIndices, 0, 0 };
D3D11_BUFFER_DESC indexBufferDesc;
indexBufferDesc.ByteWidth = sizeof(pointSpriteIndices);
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
result = mDevice->CreateBuffer(&indexBufferDesc, &indexBufferData, &mPointSpriteIndexBuffer);
if (FAILED(result))
{
SafeRelease(mPointSpriteVertexBuffer);
return gl::Error(GL_OUT_OF_MEMORY, "Failed to create instanced pointsprite emulation index buffer, HRESULT: 0x%08x", result);
}
}
// The index buffer is applied here because Instanced PointSprite emulation uses
// the a non-indexed rendering path in ANGLE (DrawArrays). This means that applyIndexBuffer()
// on the renderer will not be called and setting this buffer here ensures that the rendering
// path will contain the correct index buffers.
mDeviceContext->IASetIndexBuffer(mPointSpriteIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
}
if (moveFirstIndexedIntoSlotZero) if (moveFirstIndexedIntoSlotZero)
{ {
// In this case, we swapped the slots of the first instanced element and the first indexed element, to ensure // In this case, we swapped the slots of the first instanced element and the first indexed element, to ensure
......
...@@ -77,6 +77,9 @@ class InputLayoutCache ...@@ -77,6 +77,9 @@ class InputLayoutCache
UINT mCurrentVertexStrides[gl::MAX_VERTEX_ATTRIBS]; UINT mCurrentVertexStrides[gl::MAX_VERTEX_ATTRIBS];
UINT mCurrentVertexOffsets[gl::MAX_VERTEX_ATTRIBS]; UINT mCurrentVertexOffsets[gl::MAX_VERTEX_ATTRIBS];
ID3D11Buffer *mPointSpriteVertexBuffer;
ID3D11Buffer *mPointSpriteIndexBuffer;
static std::size_t hashInputLayout(const InputLayoutKey &inputLayout); static std::size_t hashInputLayout(const InputLayoutKey &inputLayout);
static bool compareInputLayouts(const InputLayoutKey &a, const InputLayoutKey &b); static bool compareInputLayouts(const InputLayoutKey &a, const InputLayoutKey &b);
......
...@@ -928,6 +928,13 @@ void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float z ...@@ -928,6 +928,13 @@ void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float z
mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f); mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f);
mPixelConstants.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f); mPixelConstants.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f);
// Instanced pointsprite emulation requires ViewCoords to be defined in the
// the vertex shader.
mVertexConstants.viewCoords[0] = mPixelConstants.viewCoords[0];
mVertexConstants.viewCoords[1] = mPixelConstants.viewCoords[1];
mVertexConstants.viewCoords[2] = mPixelConstants.viewCoords[2];
mVertexConstants.viewCoords[3] = mPixelConstants.viewCoords[3];
mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f; mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f;
mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f; mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f;
...@@ -943,7 +950,7 @@ void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float z ...@@ -943,7 +950,7 @@ void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float z
mForceSetViewport = false; mForceSetViewport = false;
} }
bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count) bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize)
{ {
D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
...@@ -964,6 +971,14 @@ bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count) ...@@ -964,6 +971,14 @@ bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count)
return false; return false;
} }
// If instanced pointsprite emulation is being used and If gl_PointSize is used in the shader,
// GL_POINTS mode is expected to render pointsprites.
// Instanced PointSprite emulation requires that the topology to be D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST.
if (mode == GL_POINTS && usesPointSize && getWorkarounds().useInstancedPointSpriteEmulation)
{
primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
}
if (primitiveTopology != mCurrentPrimitiveTopology) if (primitiveTopology != mCurrentPrimitiveTopology)
{ {
mDeviceContext->IASetPrimitiveTopology(primitiveTopology); mDeviceContext->IASetPrimitiveTopology(primitiveTopology);
...@@ -1226,8 +1241,9 @@ void Renderer11::applyTransformFeedbackBuffers(const gl::State& state) ...@@ -1226,8 +1241,9 @@ void Renderer11::applyTransformFeedbackBuffers(const gl::State& state)
} }
} }
gl::Error Renderer11::drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive) gl::Error Renderer11::drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive, bool usesPointSize)
{ {
bool useInstancedPointSpriteEmulation = usesPointSize && getWorkarounds().useInstancedPointSpriteEmulation;
if (mode == GL_POINTS && transformFeedbackActive) if (mode == GL_POINTS && transformFeedbackActive)
{ {
// Since point sprites are generated with a geometry shader, too many vertices will // Since point sprites are generated with a geometry shader, too many vertices will
...@@ -1277,7 +1293,17 @@ gl::Error Renderer11::drawArrays(GLenum mode, GLsizei count, GLsizei instances, ...@@ -1277,7 +1293,17 @@ gl::Error Renderer11::drawArrays(GLenum mode, GLsizei count, GLsizei instances,
} }
else else
{ {
// If gl_PointSize is used and GL_POINTS is specified, then it is expected to render pointsprites.
// If instanced pointsprite emulation is being used the topology is expexted to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced must be used.
if (mode == GL_POINTS && useInstancedPointSpriteEmulation)
{
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
}
else
{
mDeviceContext->Draw(count, 0); mDeviceContext->Draw(count, 0);
}
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
} }
......
...@@ -81,7 +81,7 @@ class Renderer11 : public RendererD3D ...@@ -81,7 +81,7 @@ class Renderer11 : public RendererD3D
virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace, virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace,
bool ignoreViewport); bool ignoreViewport);
virtual bool applyPrimitiveType(GLenum mode, GLsizei count); virtual bool applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize);
gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override; gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override;
virtual gl::Error applyShaders(gl::Program *program, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer, virtual gl::Error applyShaders(gl::Program *program, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer,
bool rasterizerDiscard, bool transformFeedbackActive); bool rasterizerDiscard, bool transformFeedbackActive);
...@@ -91,7 +91,7 @@ class Renderer11 : public RendererD3D ...@@ -91,7 +91,7 @@ class Renderer11 : public RendererD3D
virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo); virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo);
virtual void applyTransformFeedbackBuffers(const gl::State &state); virtual void applyTransformFeedbackBuffers(const gl::State &state);
virtual gl::Error drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive); virtual gl::Error drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive, bool usesPointSize);
virtual gl::Error drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, virtual gl::Error drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices,
gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances); gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances);
......
...@@ -1142,6 +1142,7 @@ Workarounds GenerateWorkarounds(D3D_FEATURE_LEVEL featureLevel) ...@@ -1142,6 +1142,7 @@ Workarounds GenerateWorkarounds(D3D_FEATURE_LEVEL featureLevel)
workarounds.mrtPerfWorkaround = true; workarounds.mrtPerfWorkaround = true;
workarounds.setDataFasterThanImageUpload = true; workarounds.setDataFasterThanImageUpload = true;
workarounds.zeroMaxLodWorkaround = (featureLevel <= D3D_FEATURE_LEVEL_9_3); workarounds.zeroMaxLodWorkaround = (featureLevel <= D3D_FEATURE_LEVEL_9_3);
workarounds.useInstancedPointSpriteEmulation = (featureLevel <= D3D_FEATURE_LEVEL_9_3);
return workarounds; return workarounds;
} }
......
...@@ -1146,7 +1146,7 @@ void Renderer9::setViewport(const gl::Rectangle &viewport, float zNear, float zF ...@@ -1146,7 +1146,7 @@ void Renderer9::setViewport(const gl::Rectangle &viewport, float zNear, float zF
mForceSetViewport = false; mForceSetViewport = false;
} }
bool Renderer9::applyPrimitiveType(GLenum mode, GLsizei count) bool Renderer9::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize)
{ {
switch (mode) switch (mode)
{ {
...@@ -1389,7 +1389,7 @@ void Renderer9::applyTransformFeedbackBuffers(const gl::State& state) ...@@ -1389,7 +1389,7 @@ void Renderer9::applyTransformFeedbackBuffers(const gl::State& state)
UNREACHABLE(); UNREACHABLE();
} }
gl::Error Renderer9::drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive) gl::Error Renderer9::drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive, bool usesPointSize)
{ {
ASSERT(!transformFeedbackActive); ASSERT(!transformFeedbackActive);
......
...@@ -87,13 +87,13 @@ class Renderer9 : public RendererD3D ...@@ -87,13 +87,13 @@ class Renderer9 : public RendererD3D
virtual gl::Error applyShaders(gl::Program *program, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer, virtual gl::Error applyShaders(gl::Program *program, const gl::VertexFormat inputLayout[], const gl::Framebuffer *framebuffer,
bool rasterizerDiscard, bool transformFeedbackActive); bool rasterizerDiscard, bool transformFeedbackActive);
virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray); virtual gl::Error applyUniforms(const ProgramImpl &program, const std::vector<gl::LinkedUniform*> &uniformArray);
virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount); virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize);
virtual gl::Error applyVertexBuffer(const gl::State &state, GLint first, GLsizei count, GLsizei instances); virtual gl::Error applyVertexBuffer(const gl::State &state, GLint first, GLsizei count, GLsizei instances);
virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo); virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo);
virtual void applyTransformFeedbackBuffers(const gl::State& state); virtual void applyTransformFeedbackBuffers(const gl::State& state);
virtual gl::Error drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive); virtual gl::Error drawArrays(GLenum mode, GLsizei count, GLsizei instances, bool transformFeedbackActive, bool usesPointSize);
virtual gl::Error drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, virtual gl::Error drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices,
gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances); gl::Buffer *elementArrayBuffer, const TranslatedIndexData &indexInfo, GLsizei instances);
......
...@@ -565,6 +565,7 @@ Workarounds GenerateWorkarounds() ...@@ -565,6 +565,7 @@ Workarounds GenerateWorkarounds()
Workarounds workarounds; Workarounds workarounds;
workarounds.mrtPerfWorkaround = true; workarounds.mrtPerfWorkaround = true;
workarounds.setDataFasterThanImageUpload = false; workarounds.setDataFasterThanImageUpload = false;
workarounds.useInstancedPointSpriteEmulation = false;
return workarounds; return workarounds;
} }
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
'<(angle_path)/tests/angle_tests/media/pixel.inl', '<(angle_path)/tests/angle_tests/media/pixel.inl',
'<(angle_path)/tests/angle_tests/OcclusionQueriesTest.cpp', '<(angle_path)/tests/angle_tests/OcclusionQueriesTest.cpp',
'<(angle_path)/tests/angle_tests/PBOExtensionTest.cpp', '<(angle_path)/tests/angle_tests/PBOExtensionTest.cpp',
'<(angle_path)/tests/angle_tests/PointSpritesTest.cpp',
'<(angle_path)/tests/angle_tests/ProgramBinaryTest.cpp', '<(angle_path)/tests/angle_tests/ProgramBinaryTest.cpp',
'<(angle_path)/tests/angle_tests/ReadPixelsTest.cpp', '<(angle_path)/tests/angle_tests/ReadPixelsTest.cpp',
'<(angle_path)/tests/angle_tests/RendererTest.cpp', '<(angle_path)/tests/angle_tests/RendererTest.cpp',
......
#include "ANGLETest.h"
// Use this to select which configurations (e.g. which renderer, which GLES
// major version) these tests should be run against.
//
// Some of the pointsprite tests below were ported from Khronos WebGL
// conformance test suite.
//
// We test on D3D11 9_3 because the existing D3D11 PointSprite implementation
// uses Geometry Shaders which are not supported for 9_3.
// D3D9 and D3D11 are also tested to ensure no regressions.
ANGLE_TYPED_TEST_CASE(PointSpritesTest, ES2_D3D9, ES2_D3D11, ES2_D3D11_FL9_3);
template<typename T>
class PointSpritesTest : public ANGLETest
{
protected:
const int windowWidth = 256;
const int windowHeight = 256;
PointSpritesTest() : ANGLETest(T::GetGlesMajorVersion(), T::GetPlatform())
{
setWindowWidth(windowWidth);
setWindowHeight(windowHeight);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
virtual void SetUp()
{
ANGLETest::SetUp();
}
float s2p(float s)
{
return (s + 1.0f) * 0.5f * (GLfloat)windowWidth;
}
};
// Checks gl_PointCoord and gl_PointSize
// https://www.khronos.org/registry/webgl/sdk/tests/conformance/glsl/variables/gl-pointcoord.html
TYPED_TEST(PointSpritesTest, PointCoordAndPointSizeCompliance)
{
const std::string fs = SHADER_SOURCE
(
precision mediump float;
void main()
{
gl_FragColor = vec4(
gl_PointCoord.x,
gl_PointCoord.y,
0,
1);
}
);
const std::string vs = SHADER_SOURCE
(
attribute vec4 vPosition;
uniform float uPointSize;
void main()
{
gl_PointSize = uPointSize;
gl_Position = vPosition;
}
);
GLuint program = CompileProgram(vs, fs);
ASSERT_NE(program, 0u);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
GLfloat pointSizeRange[2] = {};
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
GLfloat maxPointSize = pointSizeRange[1];
ASSERT_TRUE(maxPointSize >= 1);
maxPointSize = floorf(maxPointSize);
ASSERT_TRUE((int)maxPointSize % 1 == 0);
maxPointSize = std::min(maxPointSize, 64.0f);
GLfloat pointWidth = maxPointSize / windowWidth;
GLfloat step = floorf(maxPointSize / 4);
GLfloat pointStep = std::max<GLfloat>(1.0f, step);
GLint pointSizeLoc = glGetUniformLocation(program, "uPointSize");
ASSERT_GL_NO_ERROR();
glUniform1f(pointSizeLoc, maxPointSize);
ASSERT_GL_NO_ERROR();
GLfloat pixelOffset = ((int)maxPointSize % 2) ? (1.0f / (GLfloat)windowWidth) : 0;
GLuint vertexObject = 0;
glGenBuffers(1, &vertexObject);
ASSERT_NE(vertexObject, 0U);
ASSERT_GL_NO_ERROR();
glBindBuffer(GL_ARRAY_BUFFER, vertexObject);
ASSERT_GL_NO_ERROR();
GLfloat thePoints[] = { -0.5 + pixelOffset, -0.5 + pixelOffset,
0.5 + pixelOffset, -0.5 + pixelOffset,
-0.5 + pixelOffset, 0.5 + pixelOffset,
0.5 + pixelOffset, 0.5 + pixelOffset };
glBufferData(GL_ARRAY_BUFFER, sizeof(thePoints), thePoints, GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_POINTS, 0, 4);
ASSERT_GL_NO_ERROR();
glDeleteBuffers(1, &vertexObject);
std::string debugText;
for (float py = 0; py < 2; ++py) {
for (float px = 0; px < 2; ++px) {
float pointX = -0.5 + px + pixelOffset;
float pointY = -0.5 + py + pixelOffset;
for (int yy = 0; yy < maxPointSize; yy += pointStep) {
for (int xx = 0; xx < maxPointSize; xx += pointStep) {
// formula for s and t from OpenGL ES 2.0 spec section 3.3
float xw = s2p(pointX);
float yw = s2p(pointY);
float u = xx / maxPointSize * 2 - 1;
float v = yy / maxPointSize * 2 - 1;
float xf = floorf(s2p(pointX + u * pointWidth));
float yf = floorf(s2p(pointY + v * pointWidth));
float s = 0.5 + (xf + 0.5 - xw) / maxPointSize;
float t = 0.5 + (yf + 0.5 - yw) / maxPointSize;
GLubyte color[4] = { floorf(s * 255), floorf((1 - t) * 255), 0, 255 };
EXPECT_PIXEL_NEAR(xf, yf, color[0], color[1], color[2], color[3], 4);
}
}
}
}
}
// Verify that drawing a point without enabling any attributes succeeds
// https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-no-attributes.html
TYPED_TEST(PointSpritesTest, PointWithoutAttributesCompliance)
{
const std::string fs = SHADER_SOURCE
(
precision mediump float;
void main()
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
);
const std::string vs = SHADER_SOURCE
(
void main()
{
gl_PointSize = 1.0;
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
);
GLuint program = CompileProgram(vs, fs);
ASSERT_NE(program, 0u);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
glDrawArrays(GL_POINTS, 0, 1);
ASSERT_GL_NO_ERROR();
// expect the center pixel to be green
EXPECT_PIXEL_EQ((windowWidth - 1) / 2, (windowHeight - 1) / 2, 0, 255, 0, 255);
}
// This is a regression test for a graphics driver bug affecting end caps on roads in MapsGL
// https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-with-gl-pointcoord-in-fragment-shader.html
TYPED_TEST(PointSpritesTest, PointCoordRegressionTest)
{
const std::string fs = SHADER_SOURCE
(
precision mediump float;
varying vec4 v_color;
void main()
{
// It seems as long as this mathematical expression references
// gl_PointCoord, the fragment's color is incorrect.
vec2 diff = gl_PointCoord - vec2(.5, .5);
if (length(diff) > 0.5)
discard;
// The point should be a solid color.
gl_FragColor = v_color;
}
);
const std::string vs = SHADER_SOURCE
(
varying vec4 v_color;
// The X and Y coordinates of the center of the point.
attribute vec2 a_vertex;
uniform float u_pointSize;
void main()
{
gl_PointSize = u_pointSize;
gl_Position = vec4(a_vertex, 0.0, 1.0);
// The color of the point.
v_color = vec4(0.0, 1.0, 0.0, 1.0);
}
);
GLuint program = CompileProgram(vs, fs);
ASSERT_NE(program, 0u);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
GLfloat pointSizeRange[2] = {};
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
GLfloat maxPointSize = pointSizeRange[1];
ASSERT_TRUE(maxPointSize > 2);
glClearColor(0, 0, 0, 1);
glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT);
GLint pointSizeLoc = glGetUniformLocation(program, "u_pointSize");
ASSERT_GL_NO_ERROR();
GLfloat pointSize = std::min<GLfloat>(20.0f, maxPointSize);
glUniform1f(pointSizeLoc, pointSize);
ASSERT_GL_NO_ERROR();
GLuint vertexObject = 0;
glGenBuffers(1, &vertexObject);
ASSERT_NE(vertexObject, 0U);
ASSERT_GL_NO_ERROR();
glBindBuffer(GL_ARRAY_BUFFER, vertexObject);
ASSERT_GL_NO_ERROR();
GLfloat thePoints[] = { 0.0f, 0.0f };
glBufferData(GL_ARRAY_BUFFER, sizeof(thePoints), thePoints, GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_POINTS, 0, 1);
ASSERT_GL_NO_ERROR();
// expect the center pixel to be green
EXPECT_PIXEL_EQ((windowWidth - 1) / 2, (windowHeight - 1) / 2, 0, 255, 0, 255);
glDeleteBuffers(1, &vertexObject);
}
// Verify GL_VERTEX_PROGRAM_POINT_SIZE is enabled
// https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-size.html
TYPED_TEST(PointSpritesTest, PointSizeEnabledCompliance)
{
const std::string fs = SHADER_SOURCE
(
precision mediump float;
varying vec4 color;
void main()
{
gl_FragColor = color;
}
);
const std::string vs = SHADER_SOURCE
(
attribute vec3 pos;
attribute vec4 colorIn;
uniform float pointSize;
varying vec4 color;
void main()
{
gl_PointSize = pointSize;
color = colorIn;
gl_Position = vec4(pos, 1.0);
}
);
GLuint program = CompileProgram(vs, fs);
ASSERT_NE(program, 0u);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
glDisable(GL_BLEND);
// The choice of (0.4, 0.4) ensures that the centers of the surrounding
// pixels are not contained within the point when it is of size 1, but
// that they definitely are when it is of size 2.
GLfloat vertices[] = { 0.4f, 0.4f, 0.0f };
GLubyte colors[] = { 255, 0, 0, 255 };
GLuint vertexObject = 0;
glGenBuffers(1, &vertexObject);
ASSERT_NE(vertexObject, 0U);
ASSERT_GL_NO_ERROR();
glBindBuffer(GL_ARRAY_BUFFER, vertexObject);
ASSERT_GL_NO_ERROR();
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(colors), NULL, GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
ASSERT_GL_NO_ERROR();
glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(colors), colors);
ASSERT_GL_NO_ERROR();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, (GLvoid*)sizeof(vertices));
glEnableVertexAttribArray(1);
GLint pointSizeLoc = glGetUniformLocation(program, "pointSize");
ASSERT_GL_NO_ERROR();
glUniform1f(pointSizeLoc, 1.0f);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_POINTS, 0, _countof(vertices) / 3);
ASSERT_GL_NO_ERROR();
// Test the pixels around the target Red pixel to ensure
// they are the expected color values
for (GLint y = 178; y < 180; ++y)
{
for (GLint x = 178; x < 180; ++x)
{
// 179x179 is expected to be a red pixel
// All others are black
GLubyte expectedColor[4] = { 0, 0, 0, 0 };
if (x == 179 && y == 179)
{
expectedColor[0] = 255;
expectedColor[3] = 255;
}
EXPECT_PIXEL_EQ(x, y, expectedColor[0], expectedColor[1], expectedColor[2], expectedColor[3]);
}
}
swapBuffers();
GLfloat pointSizeRange[2] = {};
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
if (pointSizeRange[1] >= 2.0)
{
// Draw a point of size 2 and verify it fills the appropriate region.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniform1f(pointSizeLoc, 2.0f);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_POINTS, 0, _countof(vertices) / 3);
ASSERT_GL_NO_ERROR();
// Test the pixels to ensure the target is ALL Red pixels
for (GLint y = 178; y < 180; ++y)
{
for (GLint x = 178; x < 180; ++x)
{
EXPECT_PIXEL_EQ(x, y, 255, 0, 0, 255);
}
}
}
glDeleteBuffers(1, &vertexObject);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment