static bool load_vb_buffer(struct shader_attrib *attrib, struct gs_vertex_buffer *vb, GLint id) { GLenum type; GLint width; GLuint buffer; bool success = true; buffer = get_vb_buffer(vb, attrib->type, attrib->index, &width, &type); if (!buffer) { blog(LOG_ERROR, "Vertex buffer does not have the required " "inputs for vertex shader"); return false; } if (!gl_bind_buffer(GL_ARRAY_BUFFER, buffer)) return false; glVertexAttribPointer(id, width, type, GL_TRUE, 0, 0); if (!gl_success("glVertexAttribPointer")) success = false; glEnableVertexAttribArray(id); if (!gl_success("glEnableVertexAttribArray")) success = false; if (!gl_bind_buffer(GL_ARRAY_BUFFER, 0)) success = false; return success; }
device_t device_create(struct gs_init_data *info) { struct gs_device *device = bmalloc(sizeof(struct gs_device)); memset(device, 0, sizeof(struct gs_device)); device->plat = gl_platform_create(device, info); if (!device->plat) goto fail; if (!gl_init_extensions(device)) goto fail; gl_enable(GL_CULL_FACE); glGenProgramPipelines(1, &device->pipeline); if (!gl_success("glGenProgramPipelines")) goto fail; glBindProgramPipeline(device->pipeline); if (!gl_success("glBindProgramPipeline")) goto fail; device_leavecontext(device); device->cur_swap = gl_platform_getswap(device->plat); return device; fail: blog(LOG_ERROR, "device_create (GL) failed"); bfree(device); return NULL; }
device_t device_create(struct gs_init_data *info) { struct gs_device *device = bmalloc(sizeof(struct gs_device)); memset(device, 0, sizeof(struct gs_device)); device->plat = gl_platform_create(device, info); if (!device->plat) goto fail; glGenProgramPipelines(1, &device->pipeline); if (!gl_success("glGenProgramPipelines")) goto fail; glBindProgramPipeline(device->pipeline); if (!gl_success("glBindProgramPipeline")) goto fail; #ifdef _DEBUG glEnable(GL_DEBUG_OUTPUT); if (glGetError() == GL_INVALID_ENUM) blog(LOG_DEBUG, "OpenGL debug information not available"); #endif gl_enable(GL_CULL_FACE); device_leavecontext(device); device->cur_swap = gl_platform_getswap(device->plat); return device; fail: blog(LOG_ERROR, "device_create (GL) failed"); bfree(device); return NULL; }
void device_draw(gs_device_t *device, enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts) { struct gs_index_buffer *ib = device->cur_index_buffer; GLenum topology = convert_gs_topology(draw_mode); gs_effect_t *effect = gs_get_effect(); struct gs_program *program; if (!can_render(device)) goto fail; if (effect) gs_effect_update_params(effect); program = get_shader_program(device); if (!program) goto fail; load_vb_buffers(program, device->cur_vertex_buffer); if (program != device->cur_program && device->cur_program) { glUseProgram(0); gl_success("glUseProgram (zero)"); } if (program != device->cur_program) { device->cur_program = program; glUseProgram(program->obj); if (!gl_success("glUseProgram")) goto fail; } update_viewproj_matrix(device); program_update_params(program); if (ib) { if (num_verts == 0) num_verts = (uint32_t)device->cur_index_buffer->num; glDrawElements(topology, num_verts, ib->gl_type, (const GLvoid*)(start_vert * ib->width)); if (!gl_success("glDrawElements")) goto fail; } else { if (num_verts == 0) num_verts = (uint32_t)device->cur_vertex_buffer->num; glDrawArrays(topology, start_vert, num_verts); if (!gl_success("glDrawArrays")) goto fail; } return; fail: blog(LOG_ERROR, "device_draw (GL) failed"); }
struct gl_platform *gl_platform_create(device_t device, struct gs_init_data *info) { struct gl_platform *plat = bmalloc(sizeof(struct gl_platform)); struct dummy_context dummy; int pixel_format; PIXELFORMATDESCRIPTOR pfd; memset(plat, 0, sizeof(struct gl_platform)); memset(&dummy, 0, sizeof(struct dummy_context)); if (!gl_dummy_context_init(&dummy)) goto fail; if (!gl_init_extensions(device)) goto fail; /* you have to have a dummy context open before you can actually * use wglChoosePixelFormatARB */ if (!gl_getpixelformat(dummy.hdc, info, &pixel_format, &pfd)) goto fail; gl_dummy_context_free(&dummy); if (!init_default_swap(plat, device, pixel_format, &pfd, info)) goto fail; plat->hrc = gl_init_context(plat->swap.wi->hdc); if (!plat->hrc) goto fail; if (GLEW_ARB_seamless_cube_map) { glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); gl_success("GL_TEXTURE_CUBE_MAP_SEAMLESS"); } #ifdef _DEBUG if (GLEW_AMD_debug_output) { glDebugMessageEnableAMD(0, 0, 0, NULL, true); glDebugMessageCallbackAMD(gl_debug_message_amd, device); gl_success("glDebugMessageCallback"); } #endif return plat; fail: blog(LOG_ERROR, "gl_platform_create failed"); gl_platform_destroy(plat); gl_dummy_context_free(&dummy); return NULL; }
void shader_setfloat(shader_t shader, sparam_t param, float val) { if (matching_shader(shader, param)) { glProgramUniform1f(shader->program, param->param, val); gl_success("glProgramUniform1f"); } }
void shader_setbool(shader_t shader, sparam_t param, bool val) { if (matching_shader(shader, param)) { glProgramUniform1i(shader->program, param->param, (GLint)val); gl_success("glProgramUniform1i"); } }
static void update_viewproj_matrix(struct gs_device *device) { struct gs_shader *vs = device->cur_vertex_shader; struct matrix4 cur_proj; gs_matrix_get(&device->cur_view); matrix4_copy(&cur_proj, &device->cur_proj); if (device->cur_fbo) { cur_proj.x.y = -cur_proj.x.y; cur_proj.y.y = -cur_proj.y.y; cur_proj.z.y = -cur_proj.z.y; cur_proj.t.y = -cur_proj.t.y; glFrontFace(GL_CW); } else { glFrontFace(GL_CCW); } gl_success("glFrontFace"); matrix4_mul(&device->cur_viewproj, &device->cur_view, &cur_proj); matrix4_transpose(&device->cur_viewproj, &device->cur_viewproj); if (vs->viewproj) gs_shader_set_matrix4(vs->viewproj, &device->cur_viewproj); }
void device_stage_texture(device_t device, stagesurf_t dst, texture_t src) { struct gs_texture_2d *tex2d = (struct gs_texture_2d*)src; if (!can_stage(dst, tex2d)) goto failed; if (!gl_copy_texture(device, dst->texture, GL_TEXTURE_2D, tex2d->base.texture, GL_TEXTURE_2D, dst->width, dst->height)) goto failed; if (!gl_bind_texture(GL_TEXTURE_2D, dst->texture)) goto failed; if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, dst->pack_buffer)) goto failed; glGetTexImage(GL_TEXTURE_2D, 0, dst->gl_format, dst->gl_type, 0); if (!gl_success("glGetTexImage")) goto failed; gl_bind_buffer(GL_PIXEL_PACK_BUFFER, 0); gl_bind_texture(GL_TEXTURE_2D, 0); return; failed: gl_bind_buffer(GL_PIXEL_PACK_BUFFER, 0); gl_bind_texture(GL_TEXTURE_2D, 0); blog(LOG_ERROR, "device_stage_texture (GL) failed"); }
void device_setscissorrect(device_t device, struct gs_rect *rect) { UNUSED_PARAMETER(device); glScissor(rect->x, rect->y, rect->cx, rect->cy); if (!gl_success("glScissor")) blog(LOG_ERROR, "device_setscissorrect (GL) failed"); }
void shader_setint(shader_t shader, sparam_t param, int val) { if (matching_shader(shader, param)) { glProgramUniform1i(shader->program, param->param, val); gl_success("glProgramUniform1i"); } }
static bool attach_rendertarget(struct fbo_info *fbo, gs_texture_t tex, int side) { if (fbo->cur_render_target == tex) return true; fbo->cur_render_target = tex; if (tex->type == GS_TEXTURE_2D) { glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->texture, 0); } else if (tex->type == GS_TEXTURE_CUBE) { glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, tex->texture, 0); } else { return false; } return gl_success("glFramebufferTexture2D"); }
void device_load_pixelshader(device_t device, shader_t pixelshader) { GLuint program = 0; if (device->cur_pixel_shader == pixelshader) return; if (pixelshader && pixelshader->type != SHADER_PIXEL) { blog(LOG_ERROR, "Specified shader is not a pixel shader"); goto fail; } device->cur_pixel_shader = pixelshader; if (pixelshader) program = pixelshader->program; glUseProgramStages(device->pipeline, GL_FRAGMENT_SHADER_BIT, program); if (!gl_success("glUseProgramStages")) goto fail; clear_textures(device); if (pixelshader) load_default_pixelshader_samplers(device, pixelshader); return; fail: blog(LOG_ERROR, "device_load_pixelshader (GL) failed"); }
void device_clear(gs_device_t device, uint32_t clear_flags, struct vec4 *color, float depth, uint8_t stencil) { GLbitfield gl_flags = 0; if (clear_flags & GS_CLEAR_COLOR) { glClearColor(color->x, color->y, color->z, color->w); gl_flags |= GL_COLOR_BUFFER_BIT; } if (clear_flags & GS_CLEAR_DEPTH) { glClearDepth(depth); gl_flags |= GL_DEPTH_BUFFER_BIT; } if (clear_flags & GS_CLEAR_STENCIL) { glClearStencil(stencil); gl_flags |= GL_STENCIL_BUFFER_BIT; } glClear(gl_flags); if (!gl_success("glClear")) blog(LOG_ERROR, "device_clear (GL) failed"); UNUSED_PARAMETER(device); }
/* * This automatically manages FBOs so that render targets are always given * an FBO that matches their width/height/format to maximize optimization */ static struct fbo_info *get_fbo(struct gs_device *device, texture_t tex) { size_t i; uint32_t width, height; GLuint fbo; struct fbo_info *ptr; if (!get_tex_dimensions(tex, &width, &height)) return NULL; for (i = 0; i < device->fbos.num; i++) { ptr = device->fbos.array[i]; if (ptr->width == width && ptr->height == height && ptr->format == tex->format) return ptr; } glGenFramebuffers(1, &fbo); if (!gl_success("glGenFramebuffers")) return NULL; ptr = bmalloc(sizeof(struct fbo_info)); ptr->fbo = fbo; ptr->width = width; ptr->height = height; ptr->format = tex->format; ptr->cur_render_target = NULL; ptr->cur_render_side = 0; ptr->cur_zstencil_buffer = NULL; da_push_back(device->fbos, &ptr); return ptr; }
/* * This automatically manages FBOs so that render targets are always given * an FBO that matches their width/height/format to maximize optimization */ struct fbo_info *get_fbo(struct gs_device *device, uint32_t width, uint32_t height, enum gs_color_format format) { size_t i; GLuint fbo; struct fbo_info *ptr; for (i = 0; i < device->fbos.num; i++) { ptr = device->fbos.array[i]; if (ptr->width == width && ptr->height == height && ptr->format == format) return ptr; } glGenFramebuffers(1, &fbo); if (!gl_success("glGenFramebuffers")) return NULL; ptr = bmalloc(sizeof(struct fbo_info)); ptr->fbo = fbo; ptr->width = width; ptr->height = height; ptr->format = format; ptr->cur_render_target = NULL; ptr->cur_render_side = 0; ptr->cur_zstencil_buffer = NULL; da_push_back(device->fbos, &ptr); return ptr; }
void device_load_vertexshader(device_t device, shader_t vertshader) { GLuint program = 0; vertbuffer_t cur_vb = device->cur_vertex_buffer; if (device->cur_vertex_shader == vertshader) return; if (vertshader && vertshader->type != SHADER_VERTEX) { blog(LOG_ERROR, "Specified shader is not a vertex shader"); goto fail; } /* unload and reload the vertex buffer to sync the buffers up with * the specific shader */ if (cur_vb && !vertexbuffer_load(device, NULL)) goto fail; device->cur_vertex_shader = vertshader; if (vertshader) program = vertshader->program; glUseProgramStages(device->pipeline, GL_VERTEX_SHADER_BIT, program); if (!gl_success("glUseProgramStages")) goto fail; if (cur_vb && !vertexbuffer_load(device, cur_vb)) goto fail; return; fail: blog(LOG_ERROR, "device_load_vertexshader (GL) failed"); }
void convert_sampler_info(struct gs_sampler_state *sampler, struct gs_sampler_info *info) { GLint max_anisotropy_max; convert_filter(info->filter, &sampler->min_filter, &sampler->mag_filter); sampler->address_u = convert_address_mode(info->address_u); sampler->address_v = convert_address_mode(info->address_v); sampler->address_w = convert_address_mode(info->address_w); sampler->max_anisotropy = info->max_anisotropy; max_anisotropy_max = 1; glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy_max); gl_success("glGetIntegerv(GL_MAX_TEXTURE_ANISOTROPY_MAX)"); if (1 <= sampler->max_anisotropy && sampler->max_anisotropy <= max_anisotropy_max) return; if (sampler->max_anisotropy < 1) sampler->max_anisotropy = 1; else if (sampler->max_anisotropy > max_anisotropy_max) sampler->max_anisotropy = max_anisotropy_max; blog(LOG_INFO, "convert_sampler_info: 1 <= max_anisotropy <= " "%d violated, selected: %d, set: %d", max_anisotropy_max, info->max_anisotropy, sampler->max_anisotropy); }
static bool gl_init_zsbuffer(struct gs_zstencil_buffer *zs, uint32_t width, uint32_t height) { glGenRenderbuffers(1, &zs->buffer); if (!gl_success("glGenRenderbuffers")) return false; if (!gl_bind_renderbuffer(GL_RENDERBUFFER, zs->buffer)) return false; glRenderbufferStorage(GL_RENDERBUFFER, zs->format, width, height); if (!gl_success("glRenderbufferStorage")) return false; gl_bind_renderbuffer(GL_RENDERBUFFER, 0); return true; }
void device_depthfunction(device_t device, enum gs_depth_test test) { GLenum gl_test = convert_gs_depth_test(test); glDepthFunc(gl_test); if (!gl_success("glDepthFunc")) blog(LOG_ERROR, "device_depthfunction (GL) failed"); }
void shader_setvec4(shader_t shader, sparam_t param, const struct vec4 *val) { if (matching_shader(shader, param)) { glProgramUniform4fv(shader->program, param->param, 1, val->ptr); gl_success("glProgramUniform4fv"); } }
static inline bool check_shader_pipeline_validity(device_t device) { int valid = false; glValidateProgramPipeline(device->pipeline); if (!gl_success("glValidateProgramPipeline")) return false; glGetProgramPipelineiv(device->pipeline, GL_VALIDATE_STATUS, &valid); if (!gl_success("glGetProgramPipelineiv")) return false; if (!valid) blog(LOG_ERROR, "Shader pipeline appears to be invalid"); return valid != 0; }
void shader_setmatrix4(shader_t shader, sparam_t param, const struct matrix4 *val) { if (matching_shader(shader, param)) { glProgramUniformMatrix4fv(shader->program, param->param, 1, false, val->x.ptr); gl_success("glProgramUniformMatrix4fv"); } }
void device_draw(device_t device, enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts) { struct gs_index_buffer *ib = device->cur_index_buffer; GLenum topology = convert_gs_topology(draw_mode); effect_t effect = gs_geteffect(); if (!can_render(device)) goto fail; if (effect) effect_updateparams(effect); shader_update_textures(device->cur_pixel_shader); update_viewproj_matrix(device); #ifdef _DEBUG if (!check_shader_pipeline_validity(device)) goto fail; #endif if (ib) { if (num_verts == 0) num_verts = (uint32_t)device->cur_index_buffer->num; glDrawElements(topology, num_verts, ib->gl_type, (const GLvoid*)(start_vert * ib->width)); if (!gl_success("glDrawElements")) goto fail; } else { if (num_verts == 0) num_verts = (uint32_t)device->cur_vertex_buffer->num; glDrawArrays(topology, start_vert, num_verts); if (!gl_success("glDrawArrays")) goto fail; } return; fail: blog(LOG_ERROR, "device_draw (GL) failed"); }
void device_blendfunction(device_t device, enum gs_blend_type src, enum gs_blend_type dest) { GLenum gl_src = convert_gs_blend_type(src); GLenum gl_dst = convert_gs_blend_type(dest); glBlendFunc(gl_src, gl_dst); if (!gl_success("glBlendFunc")) blog(LOG_ERROR, "device_blendfunction (GL) failed"); }
void device_depth_function(gs_device_t *device, enum gs_depth_test test) { GLenum gl_test = convert_gs_depth_test(test); glDepthFunc(gl_test); if (!gl_success("glDepthFunc")) blog(LOG_ERROR, "device_depth_function (GL) failed"); UNUSED_PARAMETER(device); }
void stagesurface_unmap(stagesurf_t stagesurf) { if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, stagesurf->pack_buffer)) return; glUnmapBuffer(GL_PIXEL_PACK_BUFFER); gl_success("glUnmapBuffer"); gl_bind_buffer(GL_PIXEL_PACK_BUFFER, 0); }
void device_stencilfunction(device_t device, enum gs_stencil_side side, enum gs_depth_test test) { GLenum gl_side = convert_gs_stencil_side(side); GLenum gl_test = convert_gs_depth_test(test); glStencilFuncSeparate(gl_side, gl_test, 0, 0xFFFFFFFF); if (!gl_success("glStencilFuncSeparate")) blog(LOG_ERROR, "device_stencilfunction (GL) failed"); }
/* Apparently for mac, PBOs won't do an asynchronous transfer unless you use * FBOs aong with glReadPixels, which is really dumb. */ void device_stage_texture(device_t device, stagesurf_t dst, texture_t src) { struct gs_texture_2d *tex2d = (struct gs_texture_2d*)src; struct fbo_info *fbo; GLint last_fbo; bool success = false; if (!can_stage(dst, tex2d)) goto failed; if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, dst->pack_buffer)) goto failed; fbo = get_fbo(device, dst->width, dst->height, dst->format); if (!gl_get_integer_v(GL_READ_FRAMEBUFFER_BINDING, &last_fbo)) goto failed_unbind_buffer; if (!gl_bind_framebuffer(GL_READ_FRAMEBUFFER, fbo->fbo)) goto failed_unbind_buffer; glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, src->gl_target, src->texture, 0); if (!gl_success("glFrameBufferTexture2D")) goto failed_unbind_all; glReadPixels(0, 0, dst->width, dst->height, dst->gl_format, dst->gl_type, 0); if (!gl_success("glReadPixels")) goto failed_unbind_all; success = true; failed_unbind_all: gl_bind_framebuffer(GL_READ_FRAMEBUFFER, last_fbo); failed_unbind_buffer: gl_bind_buffer(GL_PIXEL_PACK_BUFFER, 0); failed: if (!success) blog(LOG_ERROR, "device_stage_texture (GL) failed"); }
void gs_zstencil_destroy(gs_zstencil_t zs) { if (zs) { if (zs->buffer) { glDeleteRenderbuffers(1, &zs->buffer); gl_success("glDeleteRenderbuffers"); } bfree(zs); } }