OpenSubdiv/opensubdiv/osd/glslTransformFeedbackComputeController.cpp

435 lines
13 KiB
C++

//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
//#define OSD_DEBUG_BUILD
#include "../osd/debug.h"
#include "../osd/glslTransformFeedbackComputeController.h"
#include "../osd/glslTransformFeedbackComputeContext.h"
#include "../osd/opengl.h"
#include "../far/error.h"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iostream>
#include <sstream>
#if _MSC_VER
#define snprintf _snprintf
#endif
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
static const char *shaderSource =
#include "../osd/glslTransformFeedbackKernel.gen.h"
;
static const char *shaderDefines = ""
#ifdef OPT_CATMARK_V_IT_VEC2
"#define OPT_CATMARK_V_IT_VEC2\n"
#endif
#ifdef OPT_E0_IT_VEC4
"#define OPT_E0_IT_VEC4\n"
#endif
#ifdef OPT_E0_S_VEC2
"#define OPT_E0_S_VEC2\n"
#endif
;
// ----------------------------------------------------------------------------
static void
bindTexture(GLint sampler, GLuint texture, int unit) {
if (sampler==-1) {
return;
}
glUniform1i(sampler, unit);
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_BUFFER, texture);
glActiveTexture(GL_TEXTURE0);
}
// ----------------------------------------------------------------------------
class GLSLTransformFeedbackComputeController::KernelBundle :
NonCopyable<GLSLTransformFeedbackComputeController::KernelBundle> {
public:
KernelBundle() :
_program(0),
_uniformSizes(0),
_uniformOffsets(0),
_uniformIndices(0),
_uniformWeights(0),
_uniformStart(0),
_uniformEnd(0),
_uniformPrimvarOffset(0) { }
~KernelBundle() {
if (_program) {
glDeleteProgram(_program);
}
}
void UseProgram() const {
glUseProgram(_program);
}
bool Compile(VertexBufferDescriptor const & desc) {
_desc = VertexBufferDescriptor(0, desc.length, desc.stride);
if (_program) {
glDeleteProgram(_program);
_program=0;
}
_program = glCreateProgram();
GLuint shader = glCreateShader(GL_VERTEX_SHADER);
std::ostringstream defines;
defines << "#define LENGTH " << desc.length << "\n"
<< "#define STRIDE " << desc.stride << "\n";
std::string defineStr = defines.str();
const char *shaderSources[4] = {"#version 410\n", 0, 0, 0};
shaderSources[1] = defineStr.c_str();
shaderSources[2] = shaderDefines;
shaderSources[3] = shaderSource;
glShaderSource(shader, 4, shaderSources, NULL);
glCompileShader(shader);
glAttachShader(_program, shader);
std::vector<std::string> outputs;
std::vector<const char *> pOutputs;
{
// vertex data (may include custom vertex data) and varying data
// are stored into the same buffer, interleaved.
//
// (gl_SkipComponents1)
// outVertexData[0]
// outVertexData[1]
// outVertexData[2]
// (gl_SkipComponents1)
//
// note that "primvarOffset" in shader is still needed to read
// interleaved components even if gl_SkipComponents is used.
//
char attrName[32];
int primvarOffset = (desc.offset % desc.stride);
for (int i = 0; i < primvarOffset; ++i) {
outputs.push_back("gl_SkipComponents1");
}
for (int i = 0; i < desc.length; ++i) {
snprintf(attrName, 32, "outVertexBuffer[%d]", i);
outputs.push_back(attrName);
}
for (int i = primvarOffset + desc.length; i < desc.stride; ++i) {
outputs.push_back("gl_SkipComponents1");
}
// convert to char* array
for (size_t i = 0; i < outputs.size(); ++i) {
pOutputs.push_back(&outputs[i][0]);
}
}
glTransformFeedbackVaryings(_program, (GLsizei)outputs.size(),
&pOutputs[0], GL_INTERLEAVED_ATTRIBS);
GLint linked = 0;
glLinkProgram(_program);
glGetProgramiv(_program, GL_LINK_STATUS, &linked);
if (linked == GL_FALSE) {
char buffer[1024];
glGetShaderInfoLog(shader, 1024, NULL, buffer);
Far::Error(Far::FAR_RUNTIME_ERROR, buffer);
glGetProgramInfoLog(_program, 1024, NULL, buffer);
Far::Error(Far::FAR_RUNTIME_ERROR, buffer);
glDeleteProgram(_program);
_program = 0;
return false;
}
glDeleteShader(shader);
// set uniform locations for compute kernels
_primvarBuffer = glGetUniformLocation(_program, "vertexBuffer");
_uniformSizes = glGetUniformLocation(_program, "sizes");
_uniformOffsets = glGetUniformLocation(_program, "offsets");
_uniformIndices = glGetUniformLocation(_program, "indices");
_uniformWeights = glGetUniformLocation(_program, "weights");
_uniformStart = glGetUniformLocation(_program, "batchStart");
_uniformEnd = glGetUniformLocation(_program, "batchEnd");
_uniformPrimvarOffset = glGetUniformLocation(_program, "primvarOffset");
OSD_DEBUG_CHECK_GL_ERROR("KernelBundle::Compile");
return true;
}
GLint GetPrimvarBufferLocation() const {
return _primvarBuffer;
}
GLint GetSizesLocation() const {
return _uniformSizes;
}
GLint GetOffsetsLocation() const {
return _uniformOffsets;
}
GLint GetIndicesLocation() const {
return _uniformIndices;
}
GLint GetWeightsLocation() const {
return _uniformWeights;
}
void TransformPrimvarBuffer(GLuint primvarBuffer,
int offset, int numCVs, int start, int end) const {
assert(end >= start);
// set batch range
glUniform1i(_uniformStart, start);
glUniform1i(_uniformEnd, end);
glUniform1i(_uniformPrimvarOffset, offset);
int count = end - start,
stride = _desc.stride*sizeof(float);
// note: offset includes both "batching offset" and "primvar offset".
//
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER,
0, primvarBuffer,
(start + numCVs)*stride + (offset - offset%stride)*sizeof(float),
count*stride);
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, count);
glEndTransformFeedback();
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
//OSD_DEBUG_CHECK_GL_ERROR("TransformPrimvarBuffer\n");
}
void ApplyStencilTableKernel(GLuint primvarBuffer,
int offset, int numCVs,
int start, int end) const {
TransformPrimvarBuffer(primvarBuffer,
offset, numCVs, start, end);
}
struct Match {
Match(VertexBufferDescriptor const & d) : desc(d) { }
bool operator() (KernelBundle const * kernel) {
return (desc.length==kernel->_desc.length and
desc.stride==kernel->_desc.stride);
}
VertexBufferDescriptor desc;
};
private:
GLuint _program;
GLint _primvarBuffer;
GLint _uniformSizes, // uniform paramaeters for kernels
_uniformOffsets,
_uniformIndices,
_uniformWeights,
_uniformStart, // batch
_uniformEnd,
_uniformPrimvarOffset;
VertexBufferDescriptor _desc; // primvar buffer descriptor
};
// ----------------------------------------------------------------------------
void
GLSLTransformFeedbackComputeController::bindBufferAndProgram(
GLuint & feedbackTexture) {
glEnable(GL_RASTERIZER_DISCARD);
_currentBindState.kernelBundle->UseProgram();
if (not feedbackTexture) {
glGenTextures(1, &feedbackTexture);
#if defined(GL_EXT_direct_state_access)
if (glTextureBufferEXT) {
glTextureBufferEXT(feedbackTexture, GL_TEXTURE_BUFFER, GL_R32F,
_currentBindState.buffer);
} else {
#else
{
#endif
glBindTexture(GL_TEXTURE_BUFFER, feedbackTexture);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, _currentBindState.buffer);
glBindTexture(GL_TEXTURE_BUFFER, 0);
}
}
bindTexture(
_currentBindState.kernelBundle->GetPrimvarBufferLocation(), feedbackTexture, 0);
// bind vertex array
// always create new one, to be safe with multiple contexts.
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
}
// ----------------------------------------------------------------------------
void
GLSLTransformFeedbackComputeController::bindContextStencilTables(
ComputeContext const *context, bool varying) {
GLint sizesLocation = _currentBindState.kernelBundle->GetSizesLocation(),
offsetsLocation = _currentBindState.kernelBundle->GetOffsetsLocation(),
indicesLocation = _currentBindState.kernelBundle->GetIndicesLocation(),
weightsLocation = _currentBindState.kernelBundle->GetWeightsLocation();
if (not varying) {
bindTexture(sizesLocation, context->GetVertexStencilTablesSizes(), 1);
bindTexture(offsetsLocation, context->GetVertexStencilTablesOffsets(), 2);
bindTexture(indicesLocation, context->GetVertexStencilTablesIndices(), 3);
bindTexture(weightsLocation, context->GetVertexStencilTablesWeights(), 4);
} else {
bindTexture(sizesLocation, context->GetVaryingStencilTablesSizes(), 1);
bindTexture(offsetsLocation, context->GetVaryingStencilTablesOffsets(), 2);
bindTexture(indicesLocation, context->GetVaryingStencilTablesIndices(), 3);
bindTexture(weightsLocation, context->GetVaryingStencilTablesWeights(), 4);
}
}
// ----------------------------------------------------------------------------
void
GLSLTransformFeedbackComputeController::unbindResources() {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, 0);
glDisable(GL_RASTERIZER_DISCARD);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(0);
glDeleteVertexArrays(1, &_vao);
}
// ----------------------------------------------------------------------------
GLSLTransformFeedbackComputeController::KernelBundle const *
GLSLTransformFeedbackComputeController::getKernel(
VertexBufferDescriptor const &desc) {
KernelRegistry::iterator it =
std::find_if(_kernelRegistry.begin(), _kernelRegistry.end(),
KernelBundle::Match(desc));
if (it != _kernelRegistry.end()) {
return *it;
} else {
KernelBundle * kernelBundle = new KernelBundle();
kernelBundle->Compile(desc);
_kernelRegistry.push_back(kernelBundle);
return kernelBundle;
}
}
// ----------------------------------------------------------------------------
void
GLSLTransformFeedbackComputeController::ApplyStencilTableKernel(
GLSLTransformFeedbackComputeContext const *context, int numStencils) const {
assert(context);
int start = 0;
int end = numStencils;
_currentBindState.kernelBundle->ApplyStencilTableKernel(
_currentBindState.buffer, _currentBindState.desc.offset,
context->GetNumControlVertices(),
start,
end);
}
// ----------------------------------------------------------------------------
GLSLTransformFeedbackComputeController::GLSLTransformFeedbackComputeController() :
_vertexTexture(0), _varyingTexture(0), _vao(0) {
}
GLSLTransformFeedbackComputeController::~GLSLTransformFeedbackComputeController() {
for (KernelRegistry::iterator it = _kernelRegistry.begin();
it != _kernelRegistry.end(); ++it) {
delete *it;
}
if (_vertexTexture) {
glDeleteTextures(1, &_vertexTexture);
}
if (_varyingTexture) {
glDeleteTextures(1, &_varyingTexture);
}
}
// ----------------------------------------------------------------------------
void
GLSLTransformFeedbackComputeController::Synchronize() {
glFinish();
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv