Browse Source

Initial transform feedback implementation.

Full support for EXT_transform_feedback, transform feedback objects
from ARB_transform_feedback2 and equivalent OpenGL ES 3.0 functionality.

Example usage is in src/Magnum/Test/TransformFeedbackGLTest.cpp, I'll
add some example later.
pull/77/head
Vladimír Vondruš 12 years ago
parent
commit
6f355c564d
  1. 20
      doc/opengl-mapping.dox
  2. 6
      doc/opengl-support.dox
  3. 11
      src/Magnum/AbstractShaderProgram.cpp
  4. 114
      src/Magnum/AbstractShaderProgram.h
  5. 8
      src/Magnum/Buffer.cpp
  6. 13
      src/Magnum/Buffer.h
  7. 12
      src/Magnum/CMakeLists.txt
  8. 5
      src/Magnum/Context.cpp
  9. 7
      src/Magnum/Context.h
  10. 6
      src/Magnum/Implementation/State.cpp
  11. 6
      src/Magnum/Implementation/State.h
  12. 45
      src/Magnum/Implementation/TransformFeedbackState.cpp
  13. 55
      src/Magnum/Implementation/TransformFeedbackState.h
  14. 1
      src/Magnum/Magnum.h
  15. 3
      src/Magnum/PrimitiveQuery.h
  16. 1
      src/Magnum/Test/CMakeLists.txt
  17. 406
      src/Magnum/Test/TransformFeedbackGLTest.cpp
  18. 258
      src/Magnum/TransformFeedback.cpp
  19. 375
      src/Magnum/TransformFeedback.h

20
doc/opengl-mapping.dox

@ -53,10 +53,10 @@ OpenGL function | Matching API
@fn_gl{BeginConditionalRender}, `glEndConditionalRender()` | @ref SampleQuery::beginConditionalRender(), \n @ref SampleQuery::endConditionalRender()
@fn_gl{BeginQuery}, `glEndQuery()` | @ref PrimitiveQuery::begin(), \n @ref SampleQuery::begin(), \n @ref TimeQuery::begin(), \n @ref AbstractQuery::end()
@fn_gl{BeginQueryIndexed}, `glEndQueryIndexed()` | |
@fn_gl{BeginTransformFeedback}, `glEndTransformFeedback()` | |
@fn_gl{BeginTransformFeedback}, `glEndTransformFeedback()` | @ref TransformFeedback::begin(), @ref TransformFeedback::end()
@fn_gl{BindAttribLocation} | @ref AbstractShaderProgram::bindAttributeLocation()
@fn_gl{BindBuffer} | not needed, handled internally in @ref Buffer and elsewhere
@fn_gl{BindBufferBase}, \n @fn_gl{BindBuffersBase}, \n @fn_gl{BindBufferRange}, \n @fn_gl{BindBuffersRange} | @ref Buffer::bind(), \n @ref Buffer::unbind()
@fn_gl{BindBufferBase}, \n @fn_gl{BindBuffersBase}, \n @fn_gl{BindBufferRange}, \n @fn_gl{BindBuffersRange} | @ref Buffer::bind(), \n @ref Buffer::unbind(), \n @ref TransformFeedback::attachBuffer(), \n @ref TransformFeedback::attachBuffers()
@fn_gl{BindFragDataLocation} | @ref AbstractShaderProgram::bindFragmentDataLocation()
@fn_gl{BindFragDataLocationIndexed} | @ref AbstractShaderProgram::bindFragmentDataLocationIndexed()
@fn_gl{BindFramebuffer} | @ref Framebuffer::bind()
@ -65,7 +65,7 @@ OpenGL function | Matching API
@fn_gl{BindRenderbuffer} | not needed, handled internally in @ref Renderbuffer
@fn_gl{BindSampler}, \n @fn_gl{BindSamplers} | |
@fn_gl{BindTexture}, \n @fn_gl{BindTextureUnit}, \n @fn_gl{BindTextures}, \n @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} | @ref AbstractTexture::bind()
@fn_gl{BindTransformFeedback} | |
@fn_gl{BindTransformFeedback} | not needed, handled internally in @ref TransformFeedback
@fn_gl{BindVertexArray} | not needed, handled internally in @ref Mesh
@fn_gl{BindVertexBuffer}, \n `glVertexArrayVertexBuffer()`, \n @fn_gl_extension{VertexArrayBindVertexBuffer,EXT,direct_state_access} \n @fn_gl{BindVertexBuffers}, \n `glVertexArrayVertexBuffers()` | |
@fn_gl{BlendColor} | @ref Renderer::setBlendColor()
@ -210,8 +210,8 @@ OpenGL function | Matching API
@fn_gl{GetTexLevelParameter}, \n `glGetTextureLevelParameter()`, \n @fn_gl_extension{GetTextureLevelParameter,EXT,direct_state_access} | @ref Texture::imageSize(), \n @ref TextureArray::imageSize(), \n @ref CubeMapTexture::imageSize(), \n @ref CubeMapTextureArray::imageSize(), \n @ref RectangleTexture::imageSize()
@fn_gl{GetTexParameter}, \n `glGetTextureParameter()`, \n @fn_gl_extension{GetTextureParameter,EXT,direct_state_access} | |
@fn_gl{GetTextureSubImage} | |
@fn_gl{GetTransformFeedback} | |
@fn_gl{GetTransformFeedbackVarying} | |
@fn_gl{GetTransformFeedback} | not queryable, @ref TransformFeedback::attachBuffer() and @ref TransformFeedback::attachBuffers() setters only
@fn_gl{GetTransformFeedbackVarying} | not queryable, @ref AbstractShaderProgram::setTransformFeedbackOutputs() setter only
@fn_gl{GetUniform}, \n `glGetnUniform()`, \n @fn_gl_extension{GetnUniform,ARB,robustness} | not queryable, @ref AbstractShaderProgram::setUniform() setter only
@fn_gl{GetUniformBlockIndex} | |
@fn_gl{GetUniformIndices} | |
@ -267,7 +267,7 @@ OpenGL function | Matching API
OpenGL function | Matching API
--------------------------------------- | ------------
@fn_gl{PatchParameter} | |
@fn_gl{PauseTransformFeedback}, @fn_gl{ResumeTransformFeedback} | |
@fn_gl{PauseTransformFeedback}, @fn_gl{ResumeTransformFeedback} | @ref TransformFeedback::pause(), @ref TransformFeedback::resume()
@fn_gl{PixelStore} | |
@fn_gl{PointParameter} | |
@fn_gl{PointSize} | @ref Renderer::setPointSize()
@ -325,8 +325,8 @@ OpenGL function | Matching API
@fn_gl{TexSubImage1D}, \n `glTextureSubImage1D()`, \n @fn_gl_extension{TextureSubImage1D,EXT,direct_state_access}, \n @fn_gl{TexSubImage2D}, \n `glTextureSubImage2D()`, \n @fn_gl_extension{TextureSubImage2D,EXT,direct_state_access}, \n @fn_gl{TexSubImage3D}, \n `glTextureSubImage3D()`, \n @fn_gl_extension{TextureSubImage3D,EXT,direct_state_access} | @ref Texture::setSubImage(), \n @ref TextureArray::setSubImage(), \n @ref CubeMapTexture::setSubImage(), \n @ref CubeMapTextureArray::setSubImage(), \n @ref RectangleTexture::setSubImage()
@fn_gl{TextureBarrier} | |
@fn_gl{TextureView} | |
@fn_gl{TransformFeedbackBufferBase}, \n @fn_gl{TransformFeedbackBufferRange} | |
@fn_gl{TransformFeedbackVaryings} | |
@fn_gl{TransformFeedbackBufferBase}, \n @fn_gl{TransformFeedbackBufferRange} | @ref TransformFeedback::attachBuffer(), \n @ref TransformFeedback::attachBuffers()
@fn_gl{TransformFeedbackVaryings} | @ref AbstractShaderProgram::setTransformFeedbackOutputs()
@subsection opengl-mapping-functions-u U
@ -438,6 +438,10 @@ OpenGL function | Matching API
@def_gl{MAX_TEXTURE_BUFFER_SIZE} | @ref BufferTexture::maxSize()
@def_gl_extension{MAX_TEXTURE_MAX_ANISOTROPY,EXT,texture_filter_anisotropic} | @ref Sampler::maxMaxAnisotropy()
@def_gl{MAX_TEXTURE_LOD_BIAS} | @ref AbstractTexture::maxLodBias()
@def_gl{MAX_TRANSFORM_FEEDBACK_BUFFERS} | @ref TransformFeedback::maxBuffers()
@def_gl{MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS} | @ref TransformFeedback::maxInterleavedComponents()
@def_gl{MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS} | @ref TransformFeedback::maxSeparateAttributes()
@def_gl{MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS} | @ref TransformFeedback::maxSeparateComponents()
@def_gl{MAX_UNIFORM_BLOCK_SIZE} | @ref AbstractShaderProgram::maxUniformBlockSize()
@def_gl{MAX_UNIFORM_BUFFER_BINDINGS} | @ref Buffer::maxUniformBindings()
@def_gl{MAX_UNIFORM_LOCATIONS} | @ref AbstractShaderProgram::maxUniformLocations()

6
doc/opengl-support.dox

@ -70,7 +70,7 @@ GLSL 1.30 | done
@extension{EXT,texture_shared_exponent} | done
@extension{EXT,draw_buffers2} | |
@extension{EXT,texture_integer} | done (GL 3.0 subset)
@extension{EXT,transform_feedback} | |
@extension{EXT,transform_feedback} | done
@extension{NV,depth_buffer_float} | |
@extension{NV,conditional_render} | done
@ -134,8 +134,8 @@ GLSL 4.00 | done
@extension{ARB,shader_subroutine} | |
@extension{ARB,tessellation_shader} | missing some limit queries and patch parameter specification function
@extension{ARB,texture_buffer_object_rgb32} | done
@extension{ARB,transform_feedback2} | |
@extension{ARB,transform_feedback3} | |
@extension{ARB,transform_feedback2} | missing transform feedback draw
@extension{ARB,transform_feedback3} | only advanced interleaving
@subsection opengl-support-41 OpenGL 4.1

11
src/Magnum/AbstractShaderProgram.cpp

@ -311,6 +311,17 @@ void AbstractShaderProgram::bindFragmentDataLocationIndexedInternal(const Unsign
}
#endif
#ifndef MAGNUM_TARGET_GLES2
void AbstractShaderProgram::setTransformFeedbackOutputs(const std::initializer_list<std::string> outputs, const TransformFeedbackBufferMode bufferMode) {
Containers::Array<const char*> names{outputs.size()};
Int i = 0;
for(const std::string& output: outputs) names[i++] = output.data();
glTransformFeedbackVaryings(_id, outputs.size(), names, GLenum(bufferMode));
}
#endif
bool AbstractShaderProgram::link(std::initializer_list<std::reference_wrapper<AbstractShaderProgram>> shaders) {
bool allSuccess = true;

114
src/Magnum/AbstractShaderProgram.h

@ -112,6 +112,22 @@ MyShader& setSpecularTexture(Texture2D& texture) {
return *this;
}
@endcode
- **Transform feedback setup function**, if needed, in which you bind buffers
to particular indices using @ref TransformFeedback::attachBuffer() and
similar, possibly with overloads based on desired use cases, e.g.:
@code
MyShader& setTransformFeedback(TransformFeedback& feedback, Buffer& positions, Buffer& data) {
feedback.attachBuffers(0, {positions, data});
return *this;
}
MyShader& setTransformFeedback(TransformFeedback& feedback, Int totalCount, Buffer& positions, GLintptr positionsOffset, Buffer& data, GLintptr dataOffset) {
feedback.attachBuffers(0, {
std::make_tuple(positions, positionsOffset, totalCount*sizeof(Vector3)),
std::make_tuple(data, dataOffset, totalCount*sizeof(Vector2ui))
});
return *this;
}
@endcode
@anchor AbstractShaderProgram-attribute-location
### Binding attribute location
@ -251,6 +267,53 @@ setUniform(uniformLocation("specularTexture"), 1);
3.0 and older. Use @ref Magnum::AbstractShaderProgram::setUniform(Int, const T&) "setUniform(Int, Int)"
instead.
@anchor AbstractShaderProgram-transform-feedback
### Specifying transform feedback binding points
The preferred workflow is to specify output binding points directly in the
shader code, e.g.:
@code
// GLSL 4.40, or
#extension GL_ARB_enhanced_layouts: enable
layout(xfb_buffer = 0, xfb_stride = 32) out block {
layout(xfb_offset = 0) vec3 position;
layout(xfb_offset = 16) vec3 normal;
};
layout(xfb_buffer = 1) out vec3 velocity;
@endcode
If you don't have the required extension, declare the uniforms without the
`xfb_*` qualifier and set the binding points using @ref setTransformFeedbackOutputs().
Equivalent setup for the previous code would be the following:
@code
out block {
vec3 position;
vec3 normal;
};
out vec3 velocity;
@endcode
@code
setTransformFeedbackOutputs({
// Buffer 0
"position", "gl_SkipComponents1", "normal", "gl_SkipComponents1",
// Buffer 1
"gl_NextBuffer", "velocity"
}, TransformFeedbackBufferMode::InterleavedAttributes);
@endcode
@see @ref TransformFeedback::maxInterleavedComponents(),
@ref TransformFeedback::maxSeparateAttributes(),
@ref TransformFeedback::maxSeparateComponents()
@requires_gl40 %Extension @extension{ARB,transform_feedback3} for using
`gl_NextBuffer` or `gl_SkipComponents#` names in
@ref Magnum::AbstractShaderProgram::setTransformFeedbackOutputs() "setTransformFeedbackOutputs()"
function
@requires_gl44 %Extension @extension{ARB,enhanced_layouts} for explicit
transform feedback output specification instead of using
@ref Magnum::AbstractShaderProgram::setTransformFeedbackOutputs() "setTransformFeedbackOutputs()"
@requires_gl Explicit transform feedback output specification is not available
in OpenGL ES.
@anchor AbstractShaderProgram-rendering-workflow
## Rendering workflow
@ -334,6 +397,24 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
friend struct Implementation::ShaderProgramState;
public:
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief %Buffer mode for transform feedback
*
* @see @ref setTransformFeedbackOutputs()
* @requires_gl30 %Extension @extension{EXT,transform_feedback}
* @requires_gles30 Transform feedback is not available in OpenGL ES
* 2.0
*/
enum class TransformFeedbackBufferMode: GLenum {
/** Attributes will be interleaved at one buffer binding point */
InterleavedAttributes = GL_INTERLEAVED_ATTRIBS,
/** Each attribute will be put into separate buffer binding point */
SeparateAttributes = GL_SEPARATE_ATTRIBS
};
#endif
/**
* @brief Max supported vertex attribute count
*
@ -700,6 +781,39 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
}
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Specify shader outputs to be recorded in transform feedback
* @param outputs Names of output variables
* @param bufferMode %Buffer mode
*
* Binds given output variables from vertex, geometry or tessellation
* shader to transform feedback buffer binding points. If
* @ref TransformFeedbackBufferMode::SeparateAttributes is used, each
* output is bound to separate binding point. If
* @ref TransformFeedbackBufferMode::InterleavedAttributes is used, the
* outputs are interleaved into single buffer binding point. In this
* case, special output name `gl_NextBuffer` causes the following
* output to be recorded into next buffer binding point and
* `gl_SkipComponents#` causes the transform feedback to offset the
* following output variable by `#` components.
* @see @fn_gl{TransformFeedbackVaryings}
* @deprecated_gl Preferred usage is to specify transform feedback
* outputs explicitly in the shader instead of using this
* function. See @ref AbstractShaderProgram-transform-feedback "class documentation"
* for more information.
* @requires_gl30 %Extension @extension{EXT,transform_feedback}
* @requires_gl40 %Extension @extension{ARB,transform_feedback3} for
* using `gl_NextBuffer` or `gl_SkipComponents#` names in
* @p outputs array
* @requires_gles30 Transform feedback is not available in OpenGL ES
* 2.0
* @requires_gl Special output names `gl_NextBuffer` and
* `gl_SkipComponents#` are not available in OpenGL ES
*/
void setTransformFeedbackOutputs(std::initializer_list<std::string> outputs, TransformFeedbackBufferMode bufferMode);
#endif
/**
* @brief Link the shader
*

8
src/Magnum/Buffer.cpp

@ -144,7 +144,7 @@ void Buffer::unbind(const Target target, const UnsignedInt firstIndex, const std
/** @todoc const std::initializer_list makes Doxygen grumpy */
void Buffer::bind(const Target target, const UnsignedInt firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform);
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform || GLenum(target) == GL_TRANSFORM_FEEDBACK_BUFFER);
#endif
Context::current()->state().buffer->bindRangesImplementation(target, firstIndex, {buffers.begin(), buffers.size()});
}
@ -152,7 +152,7 @@ void Buffer::bind(const Target target, const UnsignedInt firstIndex, std::initia
/** @todoc const std::initializer_list makes Doxygen grumpy */
void Buffer::bind(const Target target, const UnsignedInt firstIndex, std::initializer_list<Buffer*> buffers) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform);
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform || GLenum(target) == GL_TRANSFORM_FEEDBACK_BUFFER);
#endif
Context::current()->state().buffer->bindBasesImplementation(target, firstIndex, {buffers.begin(), buffers.size()});
}
@ -262,7 +262,7 @@ auto Buffer::bindSomewhereInternal(const TargetHint hint) -> TargetHint {
#ifndef MAGNUM_TARGET_GLES2
Buffer& Buffer::bind(const Target target, const UnsignedInt index, const GLintptr offset, const GLsizeiptr size) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform);
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform || GLenum(target) == GL_TRANSFORM_FEEDBACK_BUFFER);
#endif
glBindBufferRange(GLenum(target), index, _id, offset, size);
return *this;
@ -270,7 +270,7 @@ Buffer& Buffer::bind(const Target target, const UnsignedInt index, const GLintpt
Buffer& Buffer::bind(const Target target, const UnsignedInt index) {
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform);
CORRADE_INTERNAL_ASSERT(target == Target::AtomicCounter || target == Target::ShaderStorage || target == Target::Uniform || GLenum(target) == GL_TRANSFORM_FEEDBACK_BUFFER);
#endif
glBindBufferBase(GLenum(target), index, _id);
return *this;

13
src/Magnum/Buffer.h

@ -701,8 +701,8 @@ class MAGNUM_EXPORT Buffer: public AbstractObject {
* @see @ref bind(Target, UnsignedInt, GLintptr, GLsizeiptr),
* @ref maxAtomicCounterBindings(), @ref maxShaderStorageBindings(),
* @ref maxUniformBindings(), @ref shaderStorageOffsetAlignment(),
* @ref uniformOffsetAlignment(), @fn_gl{BindBuffersRange} or
* @fn_gl{BindBufferRange}
* @ref uniformOffsetAlignment(), @ref TransformFeedback::attachBuffers(),
* @fn_gl{BindBuffersRange} or @fn_gl{BindBufferRange}
* @requires_gl30 No form of indexed buffer binding is available in
* OpenGL 2.1, see particular @ref Magnum::Buffer::Target "Target"
* values for version requirements.
@ -727,7 +727,8 @@ class MAGNUM_EXPORT Buffer: public AbstractObject {
* for more information.
* @see @ref bind(Target, UnsignedInt), @ref maxAtomicCounterBindings(),
* @ref maxShaderStorageBindings(), @ref maxUniformBindings(),
* @fn_gl{BindBuffersBase} or @fn_gl{BindBufferBase}
* @ref TransformFeedback::attachBuffers(), @fn_gl{BindBuffersBase}
* or @fn_gl{BindBufferBase}
* @requires_gl30 No form of indexed buffer binding is available in
* OpenGL 2.1, see particular @ref Magnum::Buffer::Target "Target"
* values for version requirements.
@ -882,7 +883,8 @@ class MAGNUM_EXPORT Buffer: public AbstractObject {
* @see @ref bind(Target, UnsignedInt, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>>),
* @ref maxAtomicCounterBindings(), @ref maxShaderStorageBindings(),
* @ref maxUniformBindings(), @ref shaderStorageOffsetAlignment(),
* @ref uniformOffsetAlignment(), @fn_gl{BindBufferRange}
* @ref uniformOffsetAlignment(), @ref TransformFeedback::attachBuffer(),
* @fn_gl{BindBufferRange}
* @requires_gl30 No form of indexed buffer binding is available in
* OpenGL 2.1, see particular @ref Magnum::Buffer::Target "Target"
* values for version requirements.
@ -903,7 +905,8 @@ class MAGNUM_EXPORT Buffer: public AbstractObject {
* for more information.
* @see @ref bind(Target, UnsignedInt, std::initializer_list<Buffer*>),
* @ref maxAtomicCounterBindings(), @ref maxShaderStorageBindings(),
* @ref maxUniformBindings(), @fn_gl{BindBufferBase}
* @ref maxUniformBindings(), @ref TransformFeedback::attachBuffer(),
* @fn_gl{BindBufferBase}
* @requires_gl30 No form of indexed buffer binding is available in
* OpenGL 2.1, see particular @ref Magnum::Buffer::Target "Target"
* values for version requirements.

12
src/Magnum/CMakeLists.txt

@ -163,11 +163,19 @@ if(NOT TARGET_GLES2)
BufferImage.h
MultisampleTexture.h
PrimitiveQuery.h
TextureArray.h)
TextureArray.h
TransformFeedback.h)
list(APPEND Magnum_PRIVATE_HEADES
Implementation/TransformFeedbackState.h)
set(Magnum_SRCS ${Magnum_SRCS}
BufferImage.cpp
MultisampleTexture.cpp
TextureArray.cpp)
TextureArray.cpp
TransformFeedback.cpp
Implementation/TransformFeedbackState.cpp)
endif()
# Link in GL function pointer variables on platforms that support it

5
src/Magnum/Context.cpp

@ -50,6 +50,7 @@
#include "Implementation/MeshState.h"
#include "Implementation/ShaderProgramState.h"
#include "Implementation/TextureState.h"
#include "Implementation/TransformFeedbackState.h"
namespace Magnum {
@ -578,6 +579,10 @@ void Context::resetState(const States states) {
if(states & State::Textures)
_state->texture->reset();
#ifndef MAGNUM_TARGET_GLES2
if(states & State::TransformFeedback)
_state->transformFeedback->reset();
#endif
}
#ifndef DOXYGEN_GENERATING_OUTPUT

7
src/Magnum/Context.h

@ -163,7 +163,12 @@ class MAGNUM_EXPORT Context {
Shaders = 1 << 4,
/** Reset tracked texture-related bindings and state */
Textures = 1 << 5
Textures = 1 << 5,
#ifndef MAGNUM_TARGET_GLES2
/** Reset tracked transform feedback-related bindings */
TransformFeedback = 1 << 6
#endif
};
/**

6
src/Magnum/Implementation/State.cpp

@ -39,6 +39,9 @@
#include "ShaderState.h"
#include "ShaderProgramState.h"
#include "TextureState.h"
#ifndef MAGNUM_TARGET_GLES2
#include "TransformFeedbackState.h"
#endif
namespace Magnum { namespace Implementation {
@ -61,6 +64,9 @@ State::State(Context& context) {
shader = new ShaderState;
shaderProgram = new ShaderProgramState(context, extensions);
texture = new TextureState(context, extensions);
#ifndef MAGNUM_TARGET_GLES2
transformFeedback = new TransformFeedbackState(context, extensions);
#endif
/* Sort the features and remove duplicates */
std::sort(extensions.begin(), extensions.end());

6
src/Magnum/Implementation/State.h

@ -39,6 +39,9 @@ struct RendererState;
struct ShaderState;
struct ShaderProgramState;
struct TextureState;
#ifndef MAGNUM_TARGET_GLES2
struct TransformFeedbackState;
#endif
struct State {
/* Initializes context-based functionality */
@ -57,6 +60,9 @@ struct State {
ShaderState* shader;
ShaderProgramState* shaderProgram;
TextureState* texture;
#ifndef MAGNUM_TARGET_GLES2
TransformFeedbackState* transformFeedback;
#endif
};
}}

45
src/Magnum/Implementation/TransformFeedbackState.cpp

@ -0,0 +1,45 @@
#include "TransformFeedbackState.h"
#include "Magnum/Extensions.h"
#include "Magnum/TransformFeedback.h"
#include "State.h"
namespace Magnum { namespace Implementation {
TransformFeedbackState::TransformFeedbackState(Context& context, std::vector<std::string>& extensions): maxInterleavedComponents{0}, maxSeparateAttributes{0}, maxSeparateComponents{0}
#ifndef MAGNUM_TARGET_GLES
, maxBuffers{0}
#endif
{
#ifndef MAGNUM_TARGET_GLES
if(context.isExtensionSupported<Extensions::GL::ARB::direct_state_access>()) {
extensions.push_back(Extensions::GL::ARB::direct_state_access::string());
createImplementation = &TransformFeedback::createImplementationDSA;
attachRangeImplementation = &TransformFeedback::attachImplementationDSA;
attachBaseImplementation = &TransformFeedback::attachImplementationDSA;
attachRangesImplementation = &TransformFeedback::attachImplementationDSA;
attachBasesImplementation = &TransformFeedback::attachImplementationDSA;
} else
#endif
{
createImplementation = &TransformFeedback::createImplementationDefault;
attachRangeImplementation = &TransformFeedback::attachImplementationFallback;
attachBaseImplementation = &TransformFeedback::attachImplementationFallback;
attachRangesImplementation = &TransformFeedback::attachImplementationFallback;
attachBasesImplementation = &TransformFeedback::attachImplementationFallback;
}
#ifdef MAGNUM_TARGET_GLES
static_cast<void>(context);
static_cast<void>(extensions);
#endif
}
void TransformFeedbackState::reset() {
binding = State::DisengagedBinding;
}
}}

55
src/Magnum/Implementation/TransformFeedbackState.h

@ -0,0 +1,55 @@
#ifndef Magnum_Implementation_TransformFeedbackState_h
#define Magnum_Implementation_TransformFeedbackState_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "Magnum/Context.h"
namespace Magnum { namespace Implementation {
struct TransformFeedbackState {
explicit TransformFeedbackState(Context& context, std::vector<std::string>& extensions);
void reset();
GLint maxInterleavedComponents,
maxSeparateAttributes,
maxSeparateComponents;
#ifndef MAGNUM_TARGET_GLES
GLint maxBuffers;
#endif
GLuint binding;
void(TransformFeedback::*createImplementation)();
void(TransformFeedback::*attachRangeImplementation)(GLuint, Buffer&, GLintptr, GLsizeiptr);
void(TransformFeedback::*attachBaseImplementation)(GLuint, Buffer&);
void(TransformFeedback::*attachRangesImplementation)(GLuint, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>>);
void(TransformFeedback::*attachBasesImplementation)(GLuint, std::initializer_list<Buffer*>);
};
}}
#endif

1
src/Magnum/Magnum.h

@ -556,6 +556,7 @@ typedef TextureArray<2> Texture2DArray;
enum class TextureFormat: GLenum;
class TransformFeedback;
class Timeline;
enum class Version: Int;

3
src/Magnum/PrimitiveQuery.h

@ -58,8 +58,9 @@ UnsignedInt primitiveCount = q.result<UnsignedInt>();
@requires_gl30 %Extension @extension{EXT,transform_feedback}
@requires_gles30 Only sample queries are available on OpenGL ES 2.0.
@see @ref SampleQuery, @ref TimeQuery
@see @ref SampleQuery, @ref TimeQuery, @ref TransformFeedback
@todo glBeginQueryIndexed
@todo @extension{ARB,transform_feedback_overflow_query}
*/
class PrimitiveQuery: public AbstractQuery {
public:

1
src/Magnum/Test/CMakeLists.txt

@ -71,6 +71,7 @@ if(BUILD_GL_TESTS)
corrade_add_test(MultisampleTextureGLTest MultisampleTextureGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES})
corrade_add_test(PrimitiveQueryGLTest PrimitiveQueryGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES})
corrade_add_test(TextureArrayGLTest TextureArrayGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES})
corrade_add_test(TransformFeedbackGLTest TransformFeedbackGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES})
endif()
if(NOT MAGNUM_TARGET_GLES)

406
src/Magnum/Test/TransformFeedbackGLTest.cpp

@ -0,0 +1,406 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "Magnum/AbstractShaderProgram.h"
#include "Magnum/Buffer.h"
#include "Magnum/Mesh.h"
#include "Magnum/Shader.h"
#include "Magnum/TransformFeedback.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/Test/AbstractOpenGLTester.h"
namespace Magnum { namespace Test {
class TransformFeedbackGLTest: public AbstractOpenGLTester {
public:
explicit TransformFeedbackGLTest();
void construct();
void constructCopy();
void constructMove();
void label();
void attachBase();
void attachRange();
void attachBases();
void attachRanges();
void interleaved();
};
TransformFeedbackGLTest::TransformFeedbackGLTest() {
addTests({&TransformFeedbackGLTest::construct,
&TransformFeedbackGLTest::constructCopy,
&TransformFeedbackGLTest::constructMove,
&TransformFeedbackGLTest::label,
&TransformFeedbackGLTest::attachBase,
&TransformFeedbackGLTest::attachRange,
&TransformFeedbackGLTest::attachBases,
&TransformFeedbackGLTest::attachRanges,
&TransformFeedbackGLTest::interleaved});
}
void TransformFeedbackGLTest::construct() {
{
TransformFeedback feedback;
MAGNUM_VERIFY_NO_ERROR();
CORRADE_VERIFY(feedback.id() > 0);
}
MAGNUM_VERIFY_NO_ERROR();
}
void TransformFeedbackGLTest::constructCopy() {
CORRADE_VERIFY(!(std::is_constructible<TransformFeedback, const TransformFeedback&>{}));
CORRADE_VERIFY(!(std::is_assignable<TransformFeedback, const TransformFeedback&>{}));
}
void TransformFeedbackGLTest::constructMove() {
TransformFeedback a;
const Int id = a.id();
MAGNUM_VERIFY_NO_ERROR();
CORRADE_VERIFY(id > 0);
TransformFeedback b{std::move(a)};
CORRADE_COMPARE(a.id(), 0);
CORRADE_COMPARE(b.id(), id);
TransformFeedback c;
const Int cId = c.id();
c = std::move(b);
MAGNUM_VERIFY_NO_ERROR();
CORRADE_VERIFY(cId > 0);
CORRADE_COMPARE(b.id(), cId);
CORRADE_COMPARE(c.id(), id);
}
void TransformFeedbackGLTest::label() {
/* No-Op version is tested in AbstractObjectGLTest */
if(!Context::current()->isExtensionSupported<Extensions::GL::KHR::debug>() &&
!Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_label>())
CORRADE_SKIP("Required extension is not available");
TransformFeedback feedback;
CORRADE_COMPARE(feedback.label(), "");
MAGNUM_VERIFY_NO_ERROR();
feedback.setLabel("MyXfb");
MAGNUM_VERIFY_NO_ERROR();
CORRADE_COMPARE(feedback.label(), "MyXfb");
}
namespace {
constexpr const Vector2 inputData[] = {
{0.0f, 0.0f},
{-1.0f, 1.0f}
};
struct XfbShader: AbstractShaderProgram {
typedef Attribute<0, Vector2> Input;
explicit XfbShader();
};
XfbShader::XfbShader() {
#ifndef MAGNUM_TARGET_GLES
Shader vert(Version::GL300, Shader::Type::Vertex);
#else
Shader vert(Version::GLES300, Shader::Type::Vertex);
Shader frag(Version::GLES300, Shader::Type::Fragment);
#endif
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.addSource(
"in mediump vec2 inputData;\n"
"out mediump vec2 outputData;\n"
"void main() {\n"
" outputData = inputData + vec2(1.0, -1.0);\n"
"}\n").compile());
#ifndef MAGNUM_TARGET_GLES
attachShader(vert);
#else
/* ES for some reason needs both vertex and fragment shader */
CORRADE_INTERNAL_ASSERT_OUTPUT(frag.addSource("void main() {}\n").compile());
attachShaders({vert, frag});
#endif
bindAttributeLocation(Input::Location, "inputData");
setTransformFeedbackOutputs({"outputData"}, TransformFeedbackBufferMode::SeparateAttributes);
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
}
void TransformFeedbackGLTest::attachBase() {
XfbShader shader;
Buffer input;
input.setData(inputData, BufferUsage::StaticDraw);
Buffer output;
output.setData({nullptr, 2*sizeof(Vector2)}, BufferUsage::StaticRead);
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Points)
.addVertexBuffer(input, 0, XfbShader::Input{})
.setCount(2);
TransformFeedback feedback;
feedback.attachBuffer(0, output);
MAGNUM_VERIFY_NO_ERROR();
Renderer::enable(Renderer::Feature::RasterizerDiscard);
feedback.begin(shader, TransformFeedback::PrimitiveMode::Points);
mesh.draw(shader);
feedback.end();
MAGNUM_VERIFY_NO_ERROR();
Vector2* data = reinterpret_cast<Vector2*>(output.map(0, 2*sizeof(Vector2), Buffer::MapFlag::Read));
CORRADE_COMPARE(data[0], Vector2(1.0f, -1.0f));
CORRADE_COMPARE(data[1], Vector2(0.0f, 0.0f));
output.unmap();
}
void TransformFeedbackGLTest::attachRange() {
XfbShader shader;
Buffer input;
input.setData(inputData, BufferUsage::StaticDraw);
Buffer output;
output.setData({nullptr, 512 + 2*sizeof(Vector2)}, BufferUsage::StaticRead);
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Points)
.addVertexBuffer(input, 0, XfbShader::Input{})
.setCount(2);
TransformFeedback feedback;
feedback.attachBuffer(0, output, 256, 2*sizeof(Vector2));
MAGNUM_VERIFY_NO_ERROR();
shader.use();
Renderer::enable(Renderer::Feature::RasterizerDiscard);
feedback.begin(shader, TransformFeedback::PrimitiveMode::Points);
mesh.draw(shader);
feedback.end();
MAGNUM_VERIFY_NO_ERROR();
Vector2* data = reinterpret_cast<Vector2*>(output.map(256, 2*sizeof(Vector2), Buffer::MapFlag::Read));
CORRADE_COMPARE(data[0], Vector2(1.0f, -1.0f));
CORRADE_COMPARE(data[1], Vector2(0.0f, 0.0f));
output.unmap();
}
namespace {
struct XfbMultiShader: AbstractShaderProgram {
typedef Attribute<0, Vector2> Input;
explicit XfbMultiShader();
};
XfbMultiShader::XfbMultiShader() {
#ifndef MAGNUM_TARGET_GLES
Shader vert(Version::GL300, Shader::Type::Vertex);
#else
Shader vert(Version::GLES300, Shader::Type::Vertex);
Shader frag(Version::GLES300, Shader::Type::Fragment);
#endif
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.addSource(
"in mediump vec2 inputData;\n"
"out mediump vec2 output1;\n"
"out mediump float output2;\n"
"void main() {\n"
" output1 = inputData + vec2(1.0, -1.0);\n"
" output2 = inputData.x - inputData.y;\n"
"}\n").compile());
#ifndef MAGNUM_TARGET_GLES
attachShader(vert);
#else
/* ES for some reason needs both vertex and fragment shader */
CORRADE_INTERNAL_ASSERT_OUTPUT(frag.addSource("void main() {}\n").compile());
attachShaders({vert, frag});
#endif
bindAttributeLocation(Input::Location, "inputData");
setTransformFeedbackOutputs({"output1", "output2"}, TransformFeedbackBufferMode::SeparateAttributes);
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
}
void TransformFeedbackGLTest::attachBases() {
XfbMultiShader shader;
Buffer input;
input.setData(inputData, BufferUsage::StaticDraw);
Buffer output1, output2;
output1.setData({nullptr, 2*sizeof(Vector2)}, BufferUsage::StaticRead);
output2.setData({nullptr, 2*sizeof(Float)}, BufferUsage::StaticRead);
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Points)
.addVertexBuffer(input, 0, XfbMultiShader::Input{})
.setCount(2);
TransformFeedback feedback;
feedback.attachBuffers(0, {&output1, &output2});
MAGNUM_VERIFY_NO_ERROR();
Renderer::enable(Renderer::Feature::RasterizerDiscard);
feedback.begin(shader, TransformFeedback::PrimitiveMode::Points);
mesh.draw(shader);
feedback.end();
MAGNUM_VERIFY_NO_ERROR();
Vector2* data1 = reinterpret_cast<Vector2*>(output1.map(0, 2*sizeof(Vector2), Buffer::MapFlag::Read));
CORRADE_COMPARE(data1[0], Vector2(1.0f, -1.0f));
CORRADE_COMPARE(data1[1], Vector2(0.0f, 0.0f));
output1.unmap();
Float* data2 = reinterpret_cast<Float*>(output2.map(0, 2*sizeof(Float), Buffer::MapFlag::Read));
CORRADE_COMPARE(data2[0], 0.0f);
CORRADE_COMPARE(data2[1], -2.0f);
output2.unmap();
}
void TransformFeedbackGLTest::attachRanges() {
Buffer input;
input.setData(inputData, BufferUsage::StaticDraw);
Buffer output1, output2;
output1.setData({nullptr, 512 + 2*sizeof(Vector2)}, BufferUsage::StaticRead);
output2.setData({nullptr, 768 + 2*sizeof(Float)}, BufferUsage::StaticRead);
XfbMultiShader shader;
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Points)
.addVertexBuffer(input, 0, XfbMultiShader::Input{})
.setCount(2);
TransformFeedback feedback;
feedback.attachBuffers(0, {
std::make_tuple(&output1, 256, 2*sizeof(Vector2)),
std::make_tuple(&output2, 512, 2*sizeof(Float))
});
MAGNUM_VERIFY_NO_ERROR();
Renderer::enable(Renderer::Feature::RasterizerDiscard);
feedback.begin(shader, TransformFeedback::PrimitiveMode::Points);
mesh.draw(shader);
feedback.end();
MAGNUM_VERIFY_NO_ERROR();
Vector2* data1 = reinterpret_cast<Vector2*>(output1.map(256, 2*sizeof(Vector2), Buffer::MapFlag::Read));
CORRADE_COMPARE(data1[0], Vector2(1.0f, -1.0f));
CORRADE_COMPARE(data1[1], Vector2(0.0f, 0.0f));
output1.unmap();
Float* data2 = reinterpret_cast<Float*>(output2.map(512, 2*sizeof(Float), Buffer::MapFlag::Read));
CORRADE_COMPARE(data2[0], 0.0f);
CORRADE_COMPARE(data2[1], -2.0f);
output2.unmap();
}
void TransformFeedbackGLTest::interleaved() {
struct XfbInterleavedShader: AbstractShaderProgram {
typedef Attribute<0, Vector2> Input;
explicit XfbInterleavedShader() {
#ifndef MAGNUM_TARGET_GLES
Shader vert(Version::GL300, Shader::Type::Vertex);
#else
Shader vert(Version::GLES300, Shader::Type::Vertex);
Shader frag(Version::GLES300, Shader::Type::Fragment);
#endif
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.addSource(
"in mediump vec2 inputData;\n"
"out mediump vec2 output1;\n"
"out mediump float output2;\n"
"void main() {\n"
" output1 = inputData + vec2(1.0, -1.0);\n"
" output2 = inputData.x - inputData.y + 5.0;\n"
"}\n").compile());
#ifndef MAGNUM_TARGET_GLES
attachShader(vert);
#else
/* ES for some reason needs both vertex and fragment shader */
CORRADE_INTERNAL_ASSERT_OUTPUT(frag.addSource("void main() {}\n").compile());
attachShaders({vert, frag});
#endif
bindAttributeLocation(Input::Location, "inputData");
setTransformFeedbackOutputs({"output1", "gl_SkipComponents1", "output2"}, TransformFeedbackBufferMode::InterleavedAttributes);
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
} shader;
Buffer input;
input.setData(inputData, BufferUsage::StaticDraw);
Buffer output;
output.setData({nullptr, 4*sizeof(Vector2)}, BufferUsage::StaticRead);
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Points)
.addVertexBuffer(input, 0, XfbInterleavedShader::Input{})
.setCount(2);
TransformFeedback feedback;
feedback.attachBuffer(0, output);
MAGNUM_VERIFY_NO_ERROR();
Renderer::enable(Renderer::Feature::RasterizerDiscard);
feedback.begin(shader, TransformFeedback::PrimitiveMode::Points);
mesh.draw(shader);
feedback.end();
MAGNUM_VERIFY_NO_ERROR();
Vector2* data = reinterpret_cast<Vector2*>(output.map(0, 4*sizeof(Vector2), Buffer::MapFlag::Read));
CORRADE_COMPARE(data[0], Vector2(1.0f, -1.0f));
CORRADE_COMPARE(data[1].y(), 5.0f);
CORRADE_COMPARE(data[2], Vector2(0.0f, 0.0f));
CORRADE_COMPARE(data[3].y(), 3.0f);
output.unmap();
}
}}
CORRADE_TEST_MAIN(Magnum::Test::TransformFeedbackGLTest)

258
src/Magnum/TransformFeedback.cpp

@ -0,0 +1,258 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "TransformFeedback.h"
#ifndef MAGNUM_TARGET_GLES2
#include <Corrade/Utility/Assert.h>
#include "Magnum/AbstractShaderProgram.h"
#include "Magnum/Buffer.h"
#include "Magnum/Context.h"
#include "Magnum/Extensions.h"
#include "Magnum/Implementation/DebugState.h"
#include "Magnum/Implementation/State.h"
#include "Magnum/Implementation/TransformFeedbackState.h"
namespace Magnum {
Int TransformFeedback::maxInterleavedComponents() {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::transform_feedback>())
return 0;
#endif
GLint& value = Context::current()->state().transformFeedback->maxInterleavedComponents;
if(value == 0)
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &value);
return value;
}
Int TransformFeedback::maxSeparateAttributes() {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::transform_feedback>())
return 0;
#endif
GLint& value = Context::current()->state().transformFeedback->maxSeparateAttributes;
if(value == 0)
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &value);
return value;
}
Int TransformFeedback::maxSeparateComponents() {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::transform_feedback>())
return 0;
#endif
GLint& value = Context::current()->state().transformFeedback->maxSeparateComponents;
if(value == 0)
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &value);
return value;
}
#ifndef MAGNUM_TARGET_GLES
Int TransformFeedback::maxBuffers() {
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::transform_feedback3>())
return maxSeparateAttributes();
GLint& value = Context::current()->state().transformFeedback->maxBuffers;
if(value == 0)
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, &value);
return value;
}
#endif
TransformFeedback::TransformFeedback() {
(this->*Context::current()->state().transformFeedback->createImplementation)();
CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding);
}
void TransformFeedback::createImplementationDefault() {
glGenTransformFeedbacks(1, &_id);
_created = false;
}
#ifndef MAGNUM_TARGET_GLES
void TransformFeedback::createImplementationDSA() {
glCreateTransformFeedbacks(1, &_id);
_created = true;
}
#endif
TransformFeedback::~TransformFeedback() {
if(!_id) return;
/* If bound, remove itself from state */
GLuint& binding = Context::current()->state().transformFeedback->binding;
if(binding == _id) binding = 0;
glDeleteTransformFeedbacks(1, &_id);
}
void TransformFeedback::bindInternal() {
GLuint& bound = Context::current()->state().transformFeedback->binding;
/* Already bound, nothing to do */
if(bound == _id) return;
/* Bind the transform feedback otherwise, which will also finally create it */
bound = _id;
_created = true;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, _id);
}
inline void TransformFeedback::createIfNotAlready() {
if(_created) return;
/* glGen*() does not create the object, just reserves the name. Some
commands (such as glObjectLabel()) operate with IDs directly and they
require the object to be created. Binding the transform feedback finally
creates it. Also all EXT DSA functions implicitly create it. */
bindInternal();
CORRADE_INTERNAL_ASSERT(_created);
}
std::string TransformFeedback::label() {
createIfNotAlready();
return Context::current()->state().debug->getLabelImplementation(GL_TRANSFORM_FEEDBACK, _id);
}
TransformFeedback& TransformFeedback::setLabelInternal(const Containers::ArrayReference<const char> label) {
createIfNotAlready();
Context::current()->state().debug->labelImplementation(GL_TRANSFORM_FEEDBACK, _id, label);
return *this;
}
TransformFeedback& TransformFeedback::attachBuffer(const UnsignedInt index, Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
(this->*Context::current()->state().transformFeedback->attachRangeImplementation)(index, buffer, offset, size);
return *this;
}
TransformFeedback& TransformFeedback::attachBuffer(const UnsignedInt index, Buffer& buffer) {
(this->*Context::current()->state().transformFeedback->attachBaseImplementation)(index, buffer);
return *this;
}
void TransformFeedback::attachImplementationFallback(const GLuint index, Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
bindInternal();
buffer.bind(Buffer::Target(GL_TRANSFORM_FEEDBACK_BUFFER), index, offset, size);
}
#ifndef MAGNUM_TARGET_GLES
void TransformFeedback::attachImplementationDSA(const GLuint index, Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
glTransformFeedbackBufferRange(_id, index, buffer.id(), offset, size);
}
#endif
void TransformFeedback::attachImplementationFallback(const GLuint index, Buffer& buffer) {
bindInternal();
buffer.bind(Buffer::Target(GL_TRANSFORM_FEEDBACK_BUFFER), index);
}
#ifndef MAGNUM_TARGET_GLES
void TransformFeedback::attachImplementationDSA(const GLuint index, Buffer& buffer) {
glTransformFeedbackBufferBase(_id, index, buffer.id());
}
#endif
/** @todoc const std::initializer_list makes Doxygen grumpy */
TransformFeedback& TransformFeedback::attachBuffers(const UnsignedInt firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers) {
(this->*Context::current()->state().transformFeedback->attachRangesImplementation)(firstIndex, buffers);
return *this;
}
/** @todoc const std::initializer_list makes Doxygen grumpy */
TransformFeedback& TransformFeedback::attachBuffers(const UnsignedInt firstIndex, std::initializer_list<Buffer*> buffers) {
(this->*Context::current()->state().transformFeedback->attachBasesImplementation)(firstIndex, buffers);
return *this;
}
/** @todoc const std::initializer_list makes Doxygen grumpy */
void TransformFeedback::attachImplementationFallback(const GLuint firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers) {
bindInternal();
Buffer::bind(Buffer::Target(GL_TRANSFORM_FEEDBACK_BUFFER), firstIndex, buffers);
}
#ifndef MAGNUM_TARGET_GLES
/** @todoc const Containers::ArrayReference makes Doxygen grumpy */
void TransformFeedback::attachImplementationDSA(const GLuint firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers) {
for(std::size_t i = 0; i != buffers.size(); ++i) {
Buffer* buffer;
GLintptr offset;
GLsizeiptr size;
std::tie(buffer, offset, size) = *(buffers.begin() + i);
glTransformFeedbackBufferRange(_id, firstIndex + i, buffer ? buffer->id() : 0, offset, size);
}
}
#endif
/** @todoc const Containers::ArrayReference makes Doxygen grumpy */
void TransformFeedback::attachImplementationFallback(const GLuint firstIndex, std::initializer_list<Buffer*> buffers) {
bindInternal();
Buffer::bind(Buffer::Target(GL_TRANSFORM_FEEDBACK_BUFFER), firstIndex, buffers);
}
#ifndef MAGNUM_TARGET_GLES
/** @todoc const Containers::ArrayReference makes Doxygen grumpy */
void TransformFeedback::attachImplementationDSA(const GLuint firstIndex, std::initializer_list<Buffer*> buffers) {
for(std::size_t i = 0; i != buffers.size(); ++i)
glTransformFeedbackBufferBase(_id, firstIndex + i, *(buffers.begin() + i) ? (*(buffers.begin() + i))->id() : 0);
}
#endif
void TransformFeedback::begin(AbstractShaderProgram& shader, const PrimitiveMode mode) {
shader.use();
bindInternal();
glBeginTransformFeedback(GLenum(mode));
}
void TransformFeedback::pause() {
bindInternal();
glPauseTransformFeedback();
}
void TransformFeedback::resume() {
bindInternal();
glResumeTransformFeedback();
}
void TransformFeedback::end() {
bindInternal();
glEndTransformFeedback();
}
}
#endif

375
src/Magnum/TransformFeedback.h

@ -0,0 +1,375 @@
#ifndef Magnum_TransformFeedback_h
#define Magnum_TransformFeedback_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/Containers/Array.h>
#include "Magnum/AbstractObject.h"
#ifndef MAGNUM_TARGET_GLES2
/** @file
* @brief Class @ref Magnum::TransformFeedback
*/
#endif
#ifndef MAGNUM_TARGET_GLES2
namespace Magnum {
namespace Implementation { struct TransformFeedbackState; }
/**
@brief Transform feedback
@see @ref PrimitiveQuery
@requires_gl40 %Extension @extension{ARB,transform_feedback2}
@requires_gles30 Transform feedback is not available in OpenGL ES 2.0
@todo @extension{AMD,transform_feedback3_lines_triangles}?
*/
class MAGNUM_EXPORT TransformFeedback: public AbstractObject {
friend struct Implementation::TransformFeedbackState;
public:
/**
* @brief Transform feedback primitive mode
*
* @see @ref begin()
*/
enum class PrimitiveMode: GLenum {
/**
* Points. If no geometry shader is present, allowed only in
* combination with @ref MeshPrimitive::Points mesh primitive type.
* If geometry shader is present, allowed only in combination with
* `points` output primitive type.
*/
Points = GL_POINTS,
/**
* Lines. If no geometry shader is present, allowed only in
* combination with @ref MeshPrimitive::LineStrip,
* @ref MeshPrimitive::LineLoop, @ref MeshPrimitive::Lines,
* @ref MeshPrimitive::LineStripAdjacency and
* @ref MeshPrimitive::LinesAdjacency mesh primitive type. If
* geometry shader is present, allowed only in combination with
* `line_strip` output primitive type.
*/
Lines = GL_LINES,
/**
* Triangles. If no geometry shader is present, allowed only in
* combination with @ref MeshPrimitive::TriangleStrip,
* @ref MeshPrimitive::TriangleFan, @ref MeshPrimitive::Triangles,
* @ref MeshPrimitive::TriangleStripAdjacency and
* @ref MeshPrimitive::TrianglesAdjacency mesh primitive type. If
* geometry shader is present, allowed only in commbination with
* `triangle_strip` output primitive type.
*/
Triangles = GL_TRIANGLES
};
/**
* @brief Max supported interleaved component count
*
* The result is cached, repeated queries don't result in repeated
* OpenGL calls. If extension @extension{EXT,transform_feedback}
* (part of OpenGL 3.0) is not available, returns `0`.
* @see @fn_gl{Get} with @def_gl{MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS}
*/
static Int maxInterleavedComponents();
/**
* @brief Max supported separate attribute count
*
* The result is cached, repeated queries don't result in repeated
* OpenGL calls. If extension @extension{EXT,transform_feedback}
* (part of OpenGL 3.0) is not available, returns `0`.
* @see @fn_gl{Get} with @def_gl{MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS}
*/
static Int maxSeparateAttributes();
/**
* @brief Max supported separate component count
*
* The result is cached, repeated queries don't result in repeated
* OpenGL calls. If extension @extension{EXT,transform_feedback}
* (part of OpenGL 3.0) is not available, returns `0`.
* @see @fn_gl{Get} with @def_gl{MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS}
*/
static Int maxSeparateComponents();
#ifndef MAGNUM_TARGET_GLES
/**
* @brief Max supported buffer count
*
* The result is cached, repeated queries don't result in repeated
* OpenGL calls. If extension @extension{ARB,transform_feedback3}
* (part of OpenGL 4.0) is not available, returns the same value as
* @ref maxSeparateAttributes().
* @see @fn_gl{Get} with @def_gl{MAX_TRANSFORM_FEEDBACK_BUFFERS}
* @requires_gl Use @ref Magnum::TransformFeedback::maxSeparateAttributes() "maxSeparateAttributes()"
* in OpenGL ES.
*/
static Int maxBuffers();
#endif
/**
* @brief Constructor
*
* Creates new OpenGL transform feedback object. If
* @extension{ARB,direct_state_access} (part of OpenGL 4.5) is not
* supported, the transform feedback object is created on first use.
* @see @fn_gl{CreateTransformFeedbacks}, eventually
* @fn_gl{GenTransformFeedbacks}
*/
explicit TransformFeedback();
/** @brief Copying is not allowed */
TransformFeedback(const TransformFeedback&) = delete;
/** @brief Move constructor */
TransformFeedback(TransformFeedback&& other) noexcept;
/**
* @brief Destructor
*
* Deletes associated OpenGL transform feedback object.
* @see @fn_gl{DeleteTransformFeedbacks}
*/
~TransformFeedback();
/** @brief Copying is not allowed */
TransformFeedback& operator=(const TransformFeedback&) = delete;
/** @brief Move assignment */
TransformFeedback& operator=(TransformFeedback&& other) noexcept;
/** @brief OpenGL transform feedback ID */
GLuint id() const { return _id; }
/**
* @brief %Buffer label
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls. If OpenGL 4.3 is not supported and neither
* @extension{KHR,debug} nor @extension2{EXT,debug_label} desktop or ES
* extension is available, this function returns empty string.
* @see @fn_gl{GetObjectLabel} or @fn_gl_extension2{GetObjectLabel,EXT,debug_label}
* with @def_gl{BUFFER_OBJECT_EXT}
*/
std::string label();
/**
* @brief Set buffer label
* @return Reference to self (for method chaining)
*
* Default is empty string. If OpenGL 4.3 is not supported and neither
* @extension{KHR,debug} nor @extension2{EXT,debug_label} desktop or ES
* extension is available, this function does nothing.
* @see @ref maxLabelLength(), @fn_gl{ObjectLabel} or
* @fn_gl_extension2{LabelObject,EXT,debug_label} with
* @def_gl{TRANSFORM_FEEDBACK}
*/
TransformFeedback& setLabel(const std::string& label) {
return setLabelInternal({label.data(), label.size()});
}
/** @overload */
template<std::size_t size> TransformFeedback& setLabel(const char(&label)[size]) {
return setLabelInternal(label);
}
/**
* @brief Attach range of buffer
* @return Reference to self (for method chaining)
*
* The @p offset parameter must be aligned to 4 bytes. If on OpenGL ES
* or @extension{ARB,direct_state_access} (part of OpenGL 4.5) is not
* available, the transform feedback object is bound (if not already)
* and the operation is then done equivalently to
* @ref Buffer::bind(Buffer::Target, UnsignedInt, GLintptr, GLsizeiptr).
* @note This function is meant to be used only internally from
* @ref AbstractShaderProgram subclasses. See its documentation
* for more information.
* @see @ref attachBuffers(), @ref maxBuffers()/@ref maxSeparateAttributes(),
* @fn_gl{TransformFeedbackBufferRange}, eventually
* @fn_gl{BindTransformFeedback} and @fn_gl{BindBuffersRange} or
* @fn_gl{BindBufferRange}
*/
TransformFeedback& attachBuffer(UnsignedInt index, Buffer& buffer, GLintptr offset, GLsizeiptr size);
/**
* @brief Attach buffer
* @return Reference to self (for method chaining)
*
* If on OpenGL ES or @extension{ARB,direct_state_access} (part of
* OpenGL 4.5) is not available, the transform feedback object is bound
* (if not already) and the operation is then done equivalently to
* @ref Buffer::bind(Buffer::Target, UnsignedInt).
* @note This function is meant to be used only internally from
* @ref AbstractShaderProgram subclasses. See its documentation
* for more information.
* @see @ref attachBuffers(), @ref maxBuffers()/@ref maxSeparateAttributes(),
* @fn_gl{TransformFeedbackBufferRange}, eventually
* @fn_gl{BindTransformFeedback} and @fn_gl{BindBuffersRange} or
* @fn_gl{BindBufferRange}
*/
TransformFeedback& attachBuffer(UnsignedInt index, Buffer& buffer);
/**
* @brief Attach ranges of buffers
* @return Reference to self (for method chaining)
*
* Attches first buffer in the list to @p firstIndex, second to
* `firstIndex + 1` etc. Second parameter is offset, third is size. If
* any buffer is `nullptr`, given attachment point is detached. The
* range of indices must respect @ref maxBuffers() (@ref maxSeparateComponents()
* in OpenGL ES or if @extension{ARB,transform_feedback3} (part of
* OpenGL 4.0) is not available). The offsets must be aligned to 4
* bytes. All the buffers must have allocated data store. If on OpenGL
* ES or @extension{ARB,direct_state_access} (part of OpenGL 4.5) is
* not available, the transform feedback object is bound (if not
* already) and the operation is then done equivalently to
* @ref Buffer::bind(Buffer::Target, UnsignedInt, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>>).
* @note This function is meant to be used only internally from
* @ref AbstractShaderProgram subclasses. See its documentation
* for more information.
* @see @ref attachBuffer(), @fn_gl{TransformFeedbackBufferRange},
* eventually @fn_gl{BindTransformFeedback} and
* @fn_gl{BindBuffersRange} or @fn_gl{BindBufferRange}
*/
TransformFeedback& attachBuffers(UnsignedInt firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers);
/**
* @brief Attach buffers
* @return Reference to self (for method chaining)
*
* Attches first buffer in the list to @p firstIndex, second to
* `firstIndex + 1` etc. If any buffer is `nullptr`, given index is
* detached. The range of indices must respect @ref maxBuffers()
* (@ref maxSeparateComponents() in OpenGL ES or if
* @extension{ARB,transform_feedback3} (part of OpenGL 4.0) is not
* available). All the buffers must have allocated data store. If on
* OpenGL ES or @extension{ARB,direct_state_access} (part of OpenGL
* 4.5) is not available, the transform feedback object is bound (if
* not already) and the operation then is done equivalently to
* @ref Buffer::bind(Buffer::Target, UnsignedInt, std::initializer_list<Buffer*>).
* @note This function is meant to be used only internally from
* @ref AbstractShaderProgram subclasses. See its documentation
* for more information.
* @see @ref attachBuffer(), @fn_gl{TransformFeedbackBufferBase},
* eventually @fn_gl{BindTransformFeedback} and
* @fn_gl{BindBuffersBase} or @fn_gl{BindBufferBase}
*/
TransformFeedback& attachBuffers(UnsignedInt firstIndex, std::initializer_list<Buffer*> buffers);
/**
* @brief Begin transform feedback
* @param shader Shader from which to capture data
* @param mode Primitive mode
*
* When transform feedback is active, only shader given in @p shader
* and meshes with primitive type (or geometry shaders with output
* primitive type) compatible with @p mode can be used. Only one
* transform feedback object can be active at a time.
* @see @ref pause(), @ref end(), @fn_gl{BindTransformFeedback} and
* @fn_gl{BeginTransformFeedback}
*/
void begin(AbstractShaderProgram& shader, PrimitiveMode mode);
/**
* @brief Pause transform feedback
*
* Pausing transform feedback makes it inactive, allowing to use
* different shader, or starting another transform feedback.
* @see @ref resume(), @ref end(), @fn_gl{BindTransformFeedback} and
* @fn_gl{PauseTransformFeedback}
*/
void pause();
/**
* @brief Resume transform feedback
*
* Resumes transform feedback so the next captured data are appended to
* already captured ones. The restrictions specified for @ref begin()
* still apply after resuming. Only one transform feedback object can
* be active at a time.
* @see @ref pause(), @ref end(), @fn_gl{BindTransformFeedback} and
* @fn_gl{ResumeTransformFeedback}
*/
void resume();
/**
* @brief End transform feedback
*
* Ends transform feedback so the captured data can be used.
* @see @ref begin(), @fn_gl{BindTransformFeedback} and
* @fn_gl{EndTransformFeedback}
*/
void end();
private:
void bindInternal();
void MAGNUM_LOCAL createIfNotAlready();
void MAGNUM_LOCAL createImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL createImplementationDSA();
#endif
void MAGNUM_LOCAL attachImplementationFallback(GLuint index, Buffer& buffer, GLintptr offset, GLsizeiptr size);
void MAGNUM_LOCAL attachImplementationFallback(GLuint index, Buffer& buffer);
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL attachImplementationDSA(GLuint index, Buffer& buffer, GLintptr offset, GLsizeiptr size);
void MAGNUM_LOCAL attachImplementationDSA(GLuint index, Buffer& buffer);
#endif
void MAGNUM_LOCAL attachImplementationFallback(GLuint firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers);
void MAGNUM_LOCAL attachImplementationFallback(GLuint firstIndex, std::initializer_list<Buffer*> buffers);
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL attachImplementationDSA(GLuint firstIndex, std::initializer_list<std::tuple<Buffer*, GLintptr, GLsizeiptr>> buffers);
void MAGNUM_LOCAL attachImplementationDSA(GLuint firstIndex, std::initializer_list<Buffer*> buffers);
#endif
TransformFeedback& setLabelInternal(Containers::ArrayReference<const char> label);
GLuint _id;
bool _created; /* see createIfNotAlready() for details */
};
inline TransformFeedback::TransformFeedback(TransformFeedback&& other) noexcept: _id{other._id}, _created{other._created} {
other._id = 0;
}
inline TransformFeedback& TransformFeedback::operator=(TransformFeedback&& other) noexcept {
std::swap(_id, other._id);
std::swap(_created, other._created);
return *this;
}
}
#endif
#endif
Loading…
Cancel
Save