/** * @brief Upload data to a sub-position in already-created buffer. You could also use this function to * resize an existing buffer without uploading data, although it won't make the * buffer smaller. * @param data_offset Whether the data pointer should be offset by start or not. */ void R_UploadToSubBuffer(r_buffer_t *buffer, const size_t start, const size_t size, const void *data, const _Bool data_offset) { assert(buffer->bufnum != 0); // Check size. This is benign really, but it's usually a bug. if (!size) { Com_Warn("Attempted to upload 0 bytes to GPU\n"); return; } // Don't allow null ptrs since bufferSubData does not allow it. if (!data) { Com_Error(ERROR_DROP, "Fatal: attempted to upload null to GPU. bufferSubData does not allow this.\n"); } // offset ptr if requested if (start && data && data_offset) { data += start; } R_BindBuffer(buffer); // if the buffer isn't big enough to hold what we had already, // we have to resize the buffer const size_t total_size = start + size; if (total_size > buffer->size) { // if we passed a "start", the data is offset, so // just reset to null. This is an odd edge case and // it's fairly rare you'll be editing at the end first, // but who knows. if (start) { glBufferData(buffer->target, total_size, NULL, buffer->hint); R_GetError("Partial resize"); r_view.buffer_stats[buffer->type].num_full_uploads++; glBufferSubData(buffer->target, start, size, data); R_GetError("Partial update"); r_view.buffer_stats[buffer->type].num_partial_uploads++; } else { glBufferData(buffer->target, total_size, data, buffer->hint); R_GetError("Full resize"); r_view.buffer_stats[buffer->type].num_full_uploads++; } r_state.buffers_total_bytes -= buffer->size; r_state.buffers_total_bytes += total_size; buffer->size = total_size; } else { // just update the range we specified glBufferSubData(buffer->target, start, size, data); R_GetError("Updating existing buffer"); r_view.buffer_stats[buffer->type].num_partial_uploads++; } r_view.buffer_stats[buffer->type].size_uploaded += size; }
/* * @brief Uploads the specified image to the OpenGL implementation. Images that * do not have a GL texture reserved (which is most diffuse textures) will have * one generated for them. This flexibility allows for explicitly managed * textures (such as lightmaps) to be here as well. */ void R_UploadImage(r_image_t *image, GLenum format, byte *data) { if (!image || !data) { Com_Error(ERR_DROP, "NULL image or data\n"); } if (!image->texnum) { glGenTextures(1, &(image->texnum)); } R_BindTexture(image->texnum); if (image->type & IT_MASK_MIPMAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_image_state.filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_image_state.filter_mag); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_image_state.anisotropy); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, r_image_state.filter_mag); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, r_image_state.filter_mag); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); } glTexImage2D(GL_TEXTURE_2D, 0, format, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, data); R_RegisterMedia((r_media_t *) image); R_GetError(image->media.name); }
/* * @brief */ void R_AttributePointer(const char *name, GLuint size, GLvoid *array) { r_attribute_t attribute; R_ProgramVariable(&attribute, R_ATTRIBUTE, name); qglVertexAttribPointer(attribute.location, size, GL_FLOAT, GL_FALSE, 0, array); R_GetError(name); }
/* * @brief */ static r_program_t *R_LoadProgram(const char *name, void(*Init)(void)) { r_program_t *prog; char log[MAX_STRING_CHARS]; uint32_t e; int32_t i; for (i = 0; i < MAX_PROGRAMS; i++) { prog = &r_state.programs[i]; if (!prog->id) break; } if (i == MAX_PROGRAMS) { Com_Warn("MAX_PROGRAMS reached\n"); return NULL; } g_strlcpy(prog->name, name, sizeof(prog->name)); prog->id = qglCreateProgram(); prog->v = R_LoadShader(GL_VERTEX_SHADER, va("%s_vs.glsl", name)); prog->f = R_LoadShader(GL_FRAGMENT_SHADER, va("%s_fs.glsl", name)); if (prog->v) qglAttachShader(prog->id, prog->v->id); if (prog->f) qglAttachShader(prog->id, prog->f->id); qglLinkProgram(prog->id); qglGetProgramiv(prog->id, GL_LINK_STATUS, &e); if (!e) { qglGetProgramInfoLog(prog->id, sizeof(log) - 1, NULL, log); Com_Warn("%s: %s\n", prog->name, log); R_ShutdownProgram(prog); return NULL; } prog->Init = Init; if (prog->Init) { // invoke initialization function R_UseProgram(prog); prog->Init(); R_UseProgram(NULL); } R_GetError(prog->name); return prog; }
/* * @brief */ static void R_ShutdownProgram(r_program_t *prog) { if (prog->v) R_ShutdownShader(prog->v); if (prog->f) R_ShutdownShader(prog->f); qglDeleteProgram(prog->id); R_GetError(prog->name); memset(prog, 0, sizeof(r_program_t)); }
/* * @brief */ void R_DisableAttribute(r_attribute_t *attribute) { if (!attribute || attribute->location == -1) { Com_Warn("NULL or invalid attribute\n"); return; } if (attribute->value.i != 0) { qglDisableVertexAttribArray(attribute->location); attribute->value.i = 0; } R_GetError(attribute->name); }
/* * R_ProgramParameter1i */ void R_ProgramParameter1i(r_uniform1i_t *variable, GLint value) { if (!variable || variable->location == -1) { Com_Warn("NULL or invalid variable\n"); return; } if (variable->value.i == value) return; qglUniform1i(variable->location, value); variable->value.i = value; R_GetError(variable->name); }
/* * R_ProgramParameter3fv */ void R_ProgramParameter3fv(r_uniform3fv_t *variable, GLfloat *value) { if (!variable || variable->location == -1) { Com_Warn("NULL or invalid variable\n"); return; } if (VectorCompare(variable->value.vec3, value)) return; qglUniform3fv(variable->location, 1, value); VectorCopy(value, variable->value.vec3); R_GetError(variable->name); }
/** * @brief Upload data to an already-created buffer. You could also use this function to * resize an existing buffer without uploading data, although it won't make the * buffer smaller. */ void R_UploadToBuffer(r_buffer_t *buffer, const size_t size, const void *data) { assert(buffer->bufnum != 0); // Check size. This is benign really, but it's usually a bug. if (!size) { Com_Warn("Attempted to upload 0 bytes to GPU"); return; } R_BindBuffer(buffer); // if the buffer isn't big enough to hold what we had already, // we have to resize the buffer if (size > buffer->size) { r_state.buffers_total_bytes -= buffer->size; r_state.buffers_total_bytes += size; glBufferData(buffer->target, size, data, buffer->hint); R_GetError("Full resize"); r_view.buffer_stats[buffer->type].num_full_uploads++; buffer->size = size; } else { // just update the range we specified if (data) { glBufferSubData(buffer->target, 0, size, data); r_view.buffer_stats[buffer->type].num_partial_uploads++; R_GetError("Updating existing buffer"); } } r_view.buffer_stats[buffer->type].size_uploaded += size; }
/** * @brief */ static void R_AddStains_UploadSurfaces(gpointer key, gpointer value, gpointer userdata) { r_bsp_surface_t *surf = (r_bsp_surface_t *) value; R_BindDiffuseTexture(surf->stainmap->texnum); glTexSubImage2D(GL_TEXTURE_2D, 0, surf->lightmap_s, surf->lightmap_t, surf->lightmap_size[0], surf->lightmap_size[1], GL_RGB, GL_UNSIGNED_BYTE, surf->stainmap_buffer); R_GetError(surf->texinfo->name); // mark the surface as having been modified, so reset knows it's resettable surf->stainmap_dirty = true; }
/** * @brief */ void R_UnbindBuffer(const r_buffer_type_t type) { if (!r_state.active_buffers[type]) { return; } r_state.active_buffers[type] = 0; glBindBuffer(R_BufferTypeToTarget(type), 0); r_view.num_state_changes[R_STATE_BIND_BUFFER]++; r_view.buffer_stats[type].bound++; R_GetError(NULL); }
/* * @brief */ void R_UseProgram(r_program_t *prog) { if (!qglUseProgram || r_state.active_program == prog) return; r_state.active_program = prog; if (prog) { qglUseProgram(prog->id); if (prog->Use) // invoke use function prog->Use(); } else { qglUseProgram(0); } R_GetError(NULL); }
/** * @brief */ void R_BindBuffer(const r_buffer_t *buffer) { assert(buffer->bufnum != 0); if (r_state.active_buffers[buffer->type] == buffer->bufnum) { return; } r_state.active_buffers[buffer->type] = buffer->bufnum; glBindBuffer(buffer->target, buffer->bufnum); r_view.num_state_changes[R_STATE_BIND_BUFFER]++; r_view.buffer_stats[buffer->type].bound++; R_GetError(NULL); }
/* * @brief Free event listener for models. */ static void R_FreeModel(r_media_t *self) { r_model_t *mod = (r_model_t *) self; if (mod->vertex_buffer) qglDeleteBuffers(1, &mod->vertex_buffer); if (mod->texcoord_buffer) qglDeleteBuffers(1, &mod->texcoord_buffer); if (mod->lightmap_texcoord_buffer) qglDeleteBuffers(1, &mod->lightmap_texcoord_buffer); if (mod->normal_buffer) qglDeleteBuffers(1, &mod->normal_buffer); if (mod->tangent_buffer) qglDeleteBuffers(1, &mod->tangent_buffer); R_GetError(mod->media.name); }
/* * @brief Allocates and populates static VBO's for the specified r_model_t. */ static void R_LoadVertexBuffers(r_model_t *mod) { if (!qglGenBuffers) return; if (IS_MESH_MODEL(mod) && mod->mesh->num_frames > 1) // animated models don't use VBO return; const GLsizei v = mod->num_verts * 3 * sizeof(GLfloat); const GLsizei st = mod->num_verts * 2 * sizeof(GLfloat); const GLsizei t = mod->num_verts * 4 * sizeof(GLfloat); // load the vertex buffer objects qglGenBuffers(1, &mod->vertex_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->vertex_buffer); qglBufferData(GL_ARRAY_BUFFER, v, mod->verts, GL_STATIC_DRAW); qglGenBuffers(1, &mod->texcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->texcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, st, mod->texcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->normal_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->normal_buffer); qglBufferData(GL_ARRAY_BUFFER, v, mod->normals, GL_STATIC_DRAW); qglGenBuffers(1, &mod->tangent_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->tangent_buffer); qglBufferData(GL_ARRAY_BUFFER, t, mod->tangents, GL_STATIC_DRAW); if (mod->lightmap_texcoords) { qglGenBuffers(1, &mod->lightmap_texcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->lightmap_texcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, st, mod->lightmap_texcoords, GL_STATIC_DRAW); } qglBindBuffer(GL_ARRAY_BUFFER, 0); R_GetError(mod->media.name); }