diff options
Diffstat (limited to 'o3d/command_buffer/service/effect_gl.cc')
-rw-r--r-- | o3d/command_buffer/service/effect_gl.cc | 850 |
1 files changed, 850 insertions, 0 deletions
diff --git a/o3d/command_buffer/service/effect_gl.cc b/o3d/command_buffer/service/effect_gl.cc new file mode 100644 index 0000000..881f6b7 --- /dev/null +++ b/o3d/command_buffer/service/effect_gl.cc @@ -0,0 +1,850 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the EffectParamGL and EffectGL +// classes, as well as the effect-related GAPI functions on GL. + +#include <map> + +#include "base/cross/std_functional.h" +#include "command_buffer/service/effect_gl.h" +#include "command_buffer/service/gapi_gl.h" +#include "command_buffer/service/effect_utils.h" + +namespace command_buffer { +namespace o3d { + +EffectParamGL::EffectParamGL(effect_param::DataType data_type, + EffectGL *effect, + unsigned int param_index) + : EffectParam(data_type), + effect_(effect), + low_level_param_index_(param_index) { + DCHECK(effect_); + effect_->LinkParam(this); +} + +EffectParamGL::~EffectParamGL() { + if (effect_) + effect_->UnlinkParam(this); +} + +static effect_param::DataType CgTypeToCBType(CGtype cg_type) { + switch (cg_type) { + case CG_FLOAT: + case CG_FLOAT1: + return effect_param::kFloat1; + case CG_FLOAT2: + return effect_param::kFloat2; + case CG_FLOAT3: + return effect_param::kFloat3; + case CG_FLOAT4: + return effect_param::kFloat4; + case CG_INT: + case CG_INT1: + return effect_param::kInt; + case CG_BOOL: + case CG_BOOL1: + return effect_param::kBool; + case CG_FLOAT4x4: + return effect_param::kMatrix4; + case CG_SAMPLER: + case CG_SAMPLER1D: + case CG_SAMPLER2D: + case CG_SAMPLER3D: + case CG_SAMPLERCUBE: + return effect_param::kSampler; + case CG_TEXTURE: + return effect_param::kTexture; + default : { + DLOG(INFO) << "Cannot convert CGtype " + << cgGetTypeString(cg_type) + << " to a Param type."; + return effect_param::kUnknown; + } + } +} + +EffectParamGL *EffectParamGL::Create(EffectGL *effect, + unsigned int index) { + DCHECK(effect); + const EffectGL::LowLevelParam &low_level_param = + effect->low_level_params_[index]; + CGparameter cg_param = EffectGL::GetEitherCgParameter(low_level_param); + CGtype cg_type = cgGetParameterType(cg_param); + if (cg_type == CG_ARRAY) { + cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); + } + effect_param::DataType type = CgTypeToCBType(cg_type); + + if (type == effect_param::kUnknown) + return NULL; + return new EffectParamGL(type, effect, index); +} + +// Fills the Desc structure, appending name and semantic if any, and if enough +// room is available in the buffer. +bool EffectParamGL::GetDesc(unsigned int size, void *data) { + using effect_param::Desc; + if (size < sizeof(Desc)) // NOLINT + return false; + if (!effect_) + return false; + const EffectGL::LowLevelParam &low_level_param = + effect_->low_level_params_[low_level_param_index_]; + CGparameter cg_param = EffectGL::GetEitherCgParameter(low_level_param); + const char *name = low_level_param.name; + const char* semantic = cgGetParameterSemantic(cg_param); + int num_elements = cgGetArraySize(cg_param, 0); + unsigned int name_size = + name ? static_cast<unsigned int>(strlen(name)) + 1 : 0; + unsigned int semantic_size = semantic ? + static_cast<unsigned int>(strlen(semantic)) + 1 : 0; + unsigned int total_size = sizeof(Desc) + name_size + semantic_size; // NOLINT + + Desc *desc = static_cast<Desc *>(data); + memset(desc, 0, sizeof(*desc)); + desc->size = total_size; + desc->data_type = data_type(); + desc->data_size = GetDataSize(data_type()); + desc->name_offset = 0; + desc->name_size = name_size; + desc->semantic_offset = 0; + desc->num_elements = num_elements; + desc->semantic_size = semantic_size; + unsigned int current_offset = sizeof(Desc); + if (name && current_offset + name_size <= size) { + desc->name_offset = current_offset; + memcpy(static_cast<char *>(data) + current_offset, name, name_size); + current_offset += name_size; + } + if (semantic && current_offset + semantic_size <= size) { + desc->semantic_offset = current_offset; + memcpy(static_cast<char *>(data) + current_offset, semantic, semantic_size); + current_offset += semantic_size; + } + return true; +} + +// Sets the data into the Cg effect parameter, using the appropriate Cg call. +bool EffectParamGL::SetData(GAPIGL *gapi, + unsigned int size, + const void * data) { + if (!effect_) + return false; + + EffectGL::LowLevelParam &low_level_param = + effect_->low_level_params_[low_level_param_index_]; + + if (low_level_param.num_elements != 0) { + DLOG(ERROR) << "Attempt to set array parameter to value."; + return false; + } + + CGparameter vp_param = low_level_param.vp_param; + CGparameter fp_param = low_level_param.fp_param; + effect_param::DataType type = data_type(); + if (size < effect_param::GetDataSize(type)) + return false; + + switch (type) { + case effect_param::kFloat1: + if (vp_param) + cgSetParameter1f(vp_param, *static_cast<const float *>(data)); + if (fp_param) + cgSetParameter1f(fp_param, *static_cast<const float *>(data)); + break; + case effect_param::kFloat2: + if (vp_param) + cgSetParameter2fv(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetParameter2fv(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kFloat3: + if (vp_param) + cgSetParameter3fv(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetParameter3fv(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kFloat4: + if (vp_param) + cgSetParameter4fv(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetParameter4fv(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kMatrix4: + if (vp_param) + cgSetMatrixParameterfr(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetMatrixParameterfr(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kInt: + if (vp_param) cgSetParameter1i(vp_param, *static_cast<const int *>(data)); + if (fp_param) cgSetParameter1i(fp_param, *static_cast<const int *>(data)); + break; + case effect_param::kBool: { + int bool_value = *static_cast<const bool *>(data)?1:0; + if (vp_param) cgSetParameter1i(vp_param, bool_value); + if (fp_param) cgSetParameter1i(fp_param, bool_value); + break; + } + case effect_param::kSampler: { + DCHECK_GE(low_level_param.sampler_ids.size(), 1U); + low_level_param.sampler_ids[0] = *static_cast<const ResourceId *>(data); + if (effect_ == gapi->current_effect()) { + gapi->DirtyEffect(); + } + break; + } + default: + DLOG(ERROR) << "Invalid parameter type."; + return false; + } + return true; +} +EffectGL::EffectGL(CGprogram vertex_program, + CGprogram fragment_program) + : vertex_program_(vertex_program), + fragment_program_(fragment_program), + update_samplers_(true) { +} + +EffectGL::~EffectGL() { + for (ParamList::iterator it = params_.begin(); + it != params_.end(); ++it) { + (*it)->ResetEffect(); + } +} + +void EffectGL::LinkParam(EffectParamGL *param) { + params_.push_back(param); +} + +void EffectGL::UnlinkParam(EffectParamGL *param) { + std::remove(params_.begin(), params_.end(), param); +} + +// Rewrites vertex program assembly code to match GL semantics for clipping. +// This parses the source, breaking it down into pieces: +// - declaration ("!!ARBvp1.0") +// - comments (that contain the parameter information) +// - instructions +// - "END" token. +// Then it rewrites the instructions so that 'result.position' doesn't get +// written directly, instead it is written to a temporary variable. Then a +// transformation is done on that variable before outputing to +// 'result.position': +// - offset x an y by half a pixel (times w). +// - remap z from [0..w] to [-w..w]. +// +// Note that for the 1/2 pixel offset, we need a parameter that depends on the +// current viewport. This is done through 'program.env[0]' which is shared +// across all programs (so we only have to update it once when we change the +// viewport), because Cg won't use them currently (it uses 'program.local' +// instead). +static bool RewriteVertexProgramSource(String *source) { + String::size_type pos = source->find('\n'); + if (pos == String::npos) { + DLOG(ERROR) << "could not find program declaration"; + return false; + } + String decl(*source, 0, pos + 1); + String::size_type start_comments = pos + 1; + // skip the comments that contain the parameters etc. + for (; pos < source->size(); pos = source->find('\n', pos)) { + ++pos; + if (pos >= source->size()) + break; + if ((*source)[pos] != '#') + break; + } + if (pos >= source->size()) { + // we only found comments. + return false; + } + String comments(*source, start_comments, pos - start_comments); + + String::size_type end_token = source->find("\nEND", pos + 1); + if (end_token == String::npos) { + DLOG(ERROR) << "Compiled shader doesn't have an END token"; + return false; + } + String instructions(*source, pos, end_token + 1 - pos); + + // Replace accesses to 'result.position' by accesses to our temp variable + // '$O3D_HPOS'. + // '$' is a valid symbol for identifiers, but Cg doesn't seem to be using + // it, so we can use it to ensure we don't have name conflicts. + static const char kOutPositionRegister[] = "result.position"; + for (String::size_type i = instructions.find(kOutPositionRegister); + i < String::npos; i = instructions.find(kOutPositionRegister, i)) { + instructions.replace(i, strlen(kOutPositionRegister), "$O3D_HPOS"); + } + + *source = decl + + comments + + // .x = 1/viewport.width; .y = 1/viewport.height; .z = 2.0; + "PARAM $O3D_HELPER = program.env[0];\n" + "TEMP $O3D_HPOS;\n" + + instructions + + // hpos.x <- hpos.x + hpos.w / viewport.width; + // hpos.y <- hpos.y - hpos.w / viewport.height; + "MAD $O3D_HPOS.xy, $O3D_HELPER.xyyy, $O3D_HPOS.w, $O3D_HPOS.xyyy;\n" + // hpos.z <- hpos.z * 2 - hpos.w + "MAD $O3D_HPOS.z, $O3D_HPOS.z, $O3D_HELPER.z, -$O3D_HPOS.w;\n" + "MOV result.position, $O3D_HPOS;\n" + "END\n"; + return true; +} + +EffectGL *EffectGL::Create(GAPIGL *gapi, + const String& effect_code, + const String& vertex_program_entry, + const String& fragment_program_entry) { + CGcontext context = gapi->cg_context(); + // Compile the original vertex program once, to get the ARBVP1 assembly code. + CGprogram original_vp = cgCreateProgram( + context, CG_SOURCE, effect_code.c_str(), CG_PROFILE_ARBVP1, + vertex_program_entry.c_str(), NULL); + const char* listing = cgGetLastListing(context); + if (original_vp == NULL) { + DLOG(ERROR) << "Effect Compile Error: " << cgGetErrorString(cgGetError()) + << " : " << listing; + return NULL; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect Compile Warnings: " << listing; + } + + String vp_assembly = cgGetProgramString(original_vp, CG_COMPILED_PROGRAM); + cgDestroyProgram(original_vp); + if (!RewriteVertexProgramSource(&vp_assembly)) { + return NULL; + } + CGprogram vertex_program = cgCreateProgram( + context, CG_OBJECT, vp_assembly.c_str(), CG_PROFILE_ARBVP1, + vertex_program_entry.c_str(), NULL); + listing = cgGetLastListing(context); + if (vertex_program == NULL) { + DLOG(ERROR) << "Effect post-rewrite Compile Error: " + << cgGetErrorString(cgGetError()) << " : " << listing; + return false; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect post-rewrite compile warnings: " << listing; + } + + CHECK_GL_ERROR(); + + // If the program rewrite introduced some syntax or semantic errors, we won't + // know it until we load the program (through a GL error). + // So flush all GL errors first... + do {} while (glGetError() != GL_NO_ERROR); + + // ... Then load the program ... + cgGLLoadProgram(vertex_program); + + // ... And check for GL errors. + if (glGetError() != GL_NO_ERROR) { + DLOG(ERROR) << "Effect post-rewrite GL Error: " + << glGetString(GL_PROGRAM_ERROR_STRING_ARB) + << "\nSource: \n" + << vp_assembly; + return NULL; + } + + CGprogram fragment_program = cgCreateProgram( + context, CG_SOURCE, effect_code.c_str(), CG_PROFILE_ARBFP1, + fragment_program_entry.c_str(), NULL); + listing = cgGetLastListing(context); + if (fragment_program == NULL) { + DLOG(ERROR) << "Effect Compile Error: " + << cgGetErrorString(cgGetError()) << " : " + << listing; + return NULL; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect Compile Warnings: " << listing; + } + + cgGLLoadProgram(fragment_program); + + // Also check for GL errors, in case Cg managed to compile, but generated a + // bad program. + if (glGetError() != GL_NO_ERROR) { + DLOG(ERROR) << "Effect GL Error: " + << glGetString(GL_PROGRAM_ERROR_STRING_ARB); + return false; + } + EffectGL *effect = new EffectGL(vertex_program, fragment_program); + effect->Initialize(); + return effect; +} + +int EffectGL::GetLowLevelParamIndexByName(const char *name) { + DCHECK(name); + for (unsigned int index = 0; index < low_level_params_.size(); ++index) { + if (!strcmp(name, low_level_params_[index].name)) { + return index; + } + } + return -1; +} + +void EffectGL::AddLowLevelParams(CGprogram prog, CGenum name_space, bool vp) { + // Iterate through parameters and add them to the vector of low level + // parameters, visiting only CGparameters that have had storage allocated to + // them, and add the params to the low_level_params_ vector. + for (CGparameter cg_param = cgGetFirstParameter(prog, name_space); + cg_param != NULL; + cg_param = cgGetNextParameter(cg_param)) { + CGenum variability = cgGetParameterVariability(cg_param); + if (variability != CG_UNIFORM) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + const char *name = cgGetParameterName(cg_param); + if (!name) + continue; + + CGtype cg_type = cgGetParameterType(cg_param); + + int num_elements; + if (cg_type == CG_ARRAY) { + num_elements = cgGetArraySize(cg_param, 0); + // Substitute the first element's type for our type. + cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); + } else { + num_elements = 0; + } + + int index = GetLowLevelParamIndexByName(name); + if (index < 0) { + LowLevelParam param; + param.name = name; + param.vp_param = NULL; + param.fp_param = NULL; + param.num_elements = num_elements; + + index = low_level_params_.size(); + if (cg_type == CG_SAMPLER || + cg_type == CG_SAMPLER1D || + cg_type == CG_SAMPLER2D || + cg_type == CG_SAMPLER3D || + cg_type == CG_SAMPLERCUBE) { + sampler_params_.push_back(index); + if (num_elements == 0) { + param.sampler_ids.push_back(kInvalidResource); + } else { + param.sampler_ids.resize(num_elements); + std::vector<ResourceId>::iterator iter; + for (iter = param.sampler_ids.begin(); + iter != param.sampler_ids.end(); + ++iter) { + *iter = kInvalidResource; + } + } + } + low_level_params_.push_back(param); + } + + if (vp) { + low_level_params_[index].vp_param = cg_param; + } else { + low_level_params_[index].fp_param = cg_param; + } + } +} + +typedef std::pair<String, effect_stream::Desc> SemanticMapElement; +typedef std::map<String, effect_stream::Desc> SemanticMap; + +// The map batween the semantics on vertex program varying parameters names +// and vertex attribute indices under the VP_30 profile. +// TODO(gman): remove this. +SemanticMapElement semantic_map_array[] = { + SemanticMapElement("POSITION", + effect_stream::Desc(vertex_struct::kPosition, 0)), + SemanticMapElement("ATTR0", + effect_stream::Desc(vertex_struct::kPosition, 0)), + SemanticMapElement("BLENDWEIGHT", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR1", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("NORMAL", + effect_stream::Desc(vertex_struct::kNormal, 0)), + SemanticMapElement("ATTR2", + effect_stream::Desc(vertex_struct::kNormal, 0)), + SemanticMapElement("COLOR0", + effect_stream::Desc(vertex_struct::kColor, 0)), + SemanticMapElement("DIFFUSE", + effect_stream::Desc(vertex_struct::kColor, 0)), + SemanticMapElement("ATTR3", + effect_stream::Desc(vertex_struct::kColor, 0)), + SemanticMapElement("COLOR1", + effect_stream::Desc(vertex_struct::kColor, 1)), + SemanticMapElement("SPECULAR", + effect_stream::Desc(vertex_struct::kColor, 1)), + SemanticMapElement("ATTR4", + effect_stream::Desc(vertex_struct::kColor, 1)), + SemanticMapElement("TESSFACTOR", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("FOGCOORD", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR5", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("PSIZE", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR6", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("BLENDINDICES", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR7", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("TEXCOORD0", + effect_stream::Desc(vertex_struct::kTexCoord, 0)), + SemanticMapElement("ATTR8", + effect_stream::Desc(vertex_struct::kTexCoord, 0)), + SemanticMapElement("TEXCOORD1", + effect_stream::Desc(vertex_struct::kTexCoord, 1)), + SemanticMapElement("ATTR9", + effect_stream::Desc(vertex_struct::kTexCoord, 1)), + SemanticMapElement("TEXCOORD2", + effect_stream::Desc(vertex_struct::kTexCoord, 2)), + SemanticMapElement("ATTR10", + effect_stream::Desc(vertex_struct::kTexCoord, 2)), + SemanticMapElement("TEXCOORD3", + effect_stream::Desc(vertex_struct::kTexCoord, 3)), + SemanticMapElement("ATTR11", + effect_stream::Desc(vertex_struct::kTexCoord, 3)), + SemanticMapElement("TEXCOORD4", + effect_stream::Desc(vertex_struct::kTexCoord, 4)), + SemanticMapElement("ATTR12", + effect_stream::Desc(vertex_struct::kTexCoord, 4)), + SemanticMapElement("TEXCOORD5", + effect_stream::Desc(vertex_struct::kTexCoord, 5)), + SemanticMapElement("ATTR13", + effect_stream::Desc(vertex_struct::kTexCoord, 5)), + SemanticMapElement("TEXCOORD6", + effect_stream::Desc(vertex_struct::kTexCoord, 6)), + SemanticMapElement("TANGENT", + effect_stream::Desc(vertex_struct::kTexCoord, 6)), + SemanticMapElement("ATTR14", + effect_stream::Desc(vertex_struct::kTexCoord, 7)), + SemanticMapElement("TEXCOORD7", + effect_stream::Desc(vertex_struct::kTexCoord, 7)), + SemanticMapElement("BINORMAL", + effect_stream::Desc(vertex_struct::kTexCoord, 8)), + SemanticMapElement("ATTR15", + effect_stream::Desc(vertex_struct::kTexCoord, 8)) +}; + +static SemanticMap semantic_map(semantic_map_array, + semantic_map_array + + arraysize(semantic_map_array)); + +void EffectGL::Initialize() { + AddLowLevelParams(vertex_program_, CG_PROGRAM, true); + AddLowLevelParams(vertex_program_, CG_GLOBAL, true); + AddLowLevelParams(fragment_program_, CG_PROGRAM, false); + AddLowLevelParams(fragment_program_, CG_GLOBAL, false); + + AddStreams(vertex_program_, CG_PROGRAM); + AddStreams(vertex_program_, CG_GLOBAL); +} + +// Loop over all leaf parameters, and find the ones that are bound to a +// semantic. +void EffectGL::AddStreams(CGprogram prog, CGenum name_space) { + for (CGparameter cg_param = cgGetFirstLeafParameter(prog, name_space); + cg_param != NULL; + cg_param = cgGetNextLeafParameter(cg_param)) { + CGenum variability = cgGetParameterVariability(cg_param); + if (variability != CG_VARYING) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + const char* cg_semantic = cgGetParameterSemantic(cg_param); + if (cg_semantic == NULL) + continue; + + SemanticMap::iterator iter = semantic_map.find(String(cg_semantic)); + if (iter == semantic_map.end()) { + streams_.push_back(effect_stream::Desc( + vertex_struct::kUnknownSemantic, 0)); + } else { + streams_.push_back(iter->second); + } + } +} + +// Begins rendering with the effect, setting all the appropriate states. +bool EffectGL::Begin(GAPIGL *gapi) { + cgGLBindProgram(vertex_program_); + cgGLBindProgram(fragment_program_); + + // sampler->ApplyStates will mess with the texture binding on unit 0, so we + // do 2 passes. + // First to set the sampler states on the texture + for (unsigned int i = 0; i < sampler_params_.size(); ++i) { + unsigned int param_index = sampler_params_[i]; + std::vector<ResourceId> &ids = low_level_params_[param_index].sampler_ids; + for (std::vector<ResourceId>::iterator iter = ids.begin(); + iter != ids.end(); + ++iter) { + ResourceId id = *iter; + if (id != kInvalidResource) { + SamplerGL *sampler = gapi->GetSampler(id); + if (!sampler->ApplyStates(gapi)) { + return false; + } + } + } + } + // Second to enable/disable the sampler params. + for (unsigned int i = 0; i < sampler_params_.size(); ++i) { + unsigned int param_index = sampler_params_[i]; + const LowLevelParam &ll_param = low_level_params_[param_index]; + std::vector<ResourceId> &ids = low_level_params_[param_index].sampler_ids; + // TODO(petersont): Rewrite the following so it handles arrays of samplers + // instead of simply bailing. + if (cgGetParameterType(ll_param.fp_param) == CG_ARRAY) + return false; + for (std::vector<ResourceId>::iterator iter = ids.begin(); + iter != ids.end(); + ++iter) { + ResourceId id = *iter; + if (id != kInvalidResource) { + SamplerGL *sampler = gapi->GetSampler(id); + GLuint gl_texture = sampler->gl_texture(); + cgGLSetTextureParameter(ll_param.fp_param, gl_texture); + cgGLEnableTextureParameter(ll_param.fp_param); + } else { + cgGLSetTextureParameter(ll_param.fp_param, 0); + cgGLDisableTextureParameter(ll_param.fp_param); + } + } + } + return true; +} + +// Terminates rendering with the effect, resetting all the appropriate states. +void EffectGL::End(GAPIGL *gapi) { +} + +// Gets the parameter count from the list. +unsigned int EffectGL::GetParamCount() const { + return low_level_params_.size(); +} + +// Gets the number of input streams from the shader. +unsigned int EffectGL::GetStreamCount() const { + return streams_.size(); +} + +// Gets a handle to the selected parameter, and wraps it into an +// EffectParamGL if successful. +EffectParamGL *EffectGL::CreateParam(unsigned int index) { + if (index >= GetParamCount()) + return NULL; + return EffectParamGL::Create(this, index); +} + +// Provided enough room is available in the buffer, fills the Desc structure, +// appending name and semantic if any. +bool EffectGL::GetStreamDesc(unsigned int index, + unsigned int size, + void *data) { + using effect_stream::Desc; + if (size < sizeof(Desc) || index >= streams_.size()) // NOLINT + return false; + + Desc *desc = static_cast<Desc *>(data); + *desc = streams_[index]; + return true; +} + +// Gets a handle to the selected parameter, and wraps it into an +// EffectParamGL if successful. +EffectParamGL *EffectGL::CreateParamByName(const char *name) { + int index = GetLowLevelParamIndexByName(name); + if (index < 0) return NULL; + return EffectParamGL::Create(this, index); +} + +parse_error::ParseError GAPIGL::CreateEffect(ResourceId id, + unsigned int size, + const void *data) { + if (id == current_effect_id_) DirtyEffect(); + // Even though Assign would Destroy the effect at id, we do it explicitly in + // case the creation fails. + effects_.Destroy(id); + // Data is vp_main \0 fp_main \0 effect_text. + String vertex_program_entry; + String fragment_program_entry; + String effect_code; + if (!ParseEffectData(size, data, + &vertex_program_entry, + &fragment_program_entry, + &effect_code)) { + return parse_error::kParseInvalidArguments; + } + EffectGL * effect = EffectGL::Create(this, effect_code, + vertex_program_entry, + fragment_program_entry); + if (!effect) return parse_error::kParseInvalidArguments; + effects_.Assign(id, effect); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyEffect(ResourceId id) { + if (id == current_effect_id_) DirtyEffect(); + return effects_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetEffect(ResourceId id) { + DirtyEffect(); + current_effect_id_ = id; + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::GetParamCount(ResourceId id, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return parse_error::kParseInvalidArguments; + *static_cast<Uint32 *>(data) = effect->GetParamCount(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::CreateParam(ResourceId param_id, + ResourceId effect_id, + unsigned int index) { + EffectGL *effect = effects_.Get(effect_id); + if (!effect) return parse_error::kParseInvalidArguments; + EffectParamGL *param = effect->CreateParam(index); + if (!param) return parse_error::kParseInvalidArguments; + effect_params_.Assign(param_id, param); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::CreateParamByName(ResourceId param_id, + ResourceId effect_id, + unsigned int size, + const void *name) { + EffectGL *effect = effects_.Get(effect_id); + if (!effect) return parse_error::kParseInvalidArguments; + std::string string_name(static_cast<const char *>(name), size); + EffectParamGL *param = effect->CreateParamByName(string_name.c_str()); + if (!param) return parse_error::kParseInvalidArguments; + effect_params_.Assign(param_id, param); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyParam(ResourceId id) { + return effect_params_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetParamData(ResourceId id, + unsigned int size, + const void *data) { + EffectParamGL *param = effect_params_.Get(id); + if (!param) return parse_error::kParseInvalidArguments; + return param->SetData(this, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::GetParamDesc(ResourceId id, + unsigned int size, + void *data) { + EffectParamGL *param = effect_params_.Get(id); + if (!param) return parse_error::kParseInvalidArguments; + return param->GetDesc(size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::GetStreamCount( + ResourceId id, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return parse_error::kParseInvalidArguments; + *static_cast<Uint32 *>(data) = effect->GetStreamCount(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::GetStreamDesc(ResourceId id, + unsigned int index, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect) return parse_error::kParseInvalidArguments; + return effect->GetStreamDesc(index, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// If the current effect is valid, call End on it, and tag for revalidation. +void GAPIGL::DirtyEffect() { + if (validate_effect_) return; + DCHECK(current_effect_); + current_effect_->End(this); + current_effect_ = NULL; + validate_effect_ = true; +} + +// Gets the current effect, and calls Begin on it (if successful). +// Should only be called if the current effect is not valid. +bool GAPIGL::ValidateEffect() { + DCHECK(validate_effect_); + DCHECK(!current_effect_); + current_effect_ = effects_.Get(current_effect_id_); + if (!current_effect_) return false; + validate_effect_ = false; + return current_effect_->Begin(this); +} + +} // namespace o3d +} // namespace command_buffer |