Changeset View
Changeset View
Standalone View
Standalone View
source/blender/gpu/intern/gpu_framebuffer.c
| Context not available. | |||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_math_base.h" | |||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| Context not available. | |||||
| GLuint object; | GLuint object; | ||||
| GPUTexture *colortex[GPU_FB_MAX_SLOTS]; | GPUTexture *colortex[GPU_FB_MAX_SLOTS]; | ||||
| GPUTexture *depthtex; | GPUTexture *depthtex; | ||||
| GPURenderBuffer *colorrb[GPU_FB_MAX_SLOTS]; | |||||
| GPURenderBuffer *depthrb; | |||||
| }; | }; | ||||
| static void GPU_print_framebuffer_error(GLenum status, char err_out[256]) | static void GPU_print_framebuffer_error(GLenum status, char err_out[256]) | ||||
| Context not available. | |||||
| int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) | int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) | ||||
| { | { | ||||
| return GPU_framebuffer_texture_attach_target(fb, tex, GPU_texture_target(tex), slot, err_out); | |||||
| } | |||||
| int GPU_framebuffer_texture_attach_target(GPUFrameBuffer *fb, GPUTexture *tex, int target, int slot, char err_out[256]) | |||||
| { | |||||
| GLenum attachment; | GLenum attachment; | ||||
| GLenum error; | GLenum error; | ||||
| Context not available. | |||||
| while (glGetError() != GL_NO_ERROR) {} | while (glGetError() != GL_NO_ERROR) {} | ||||
| glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, | glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, | ||||
| GPU_texture_target(tex), GPU_texture_opengl_bindcode(tex), 0); | target, GPU_texture_opengl_bindcode(tex), 0); | ||||
| error = glGetError(); | error = glGetError(); | ||||
| Context not available. | |||||
| void GPU_framebuffer_texture_detach(GPUTexture *tex) | void GPU_framebuffer_texture_detach(GPUTexture *tex) | ||||
| { | { | ||||
| GPU_framebuffer_texture_detach_target(tex, GPU_texture_target(tex)); | |||||
| } | |||||
| void GPU_framebuffer_texture_detach_target(GPUTexture *tex, int target) | |||||
| { | |||||
| GLenum attachment; | GLenum attachment; | ||||
| GPUFrameBuffer *fb = GPU_texture_framebuffer(tex); | GPUFrameBuffer *fb = GPU_texture_framebuffer(tex); | ||||
| int fb_attachment = GPU_texture_framebuffer_attachment(tex); | int fb_attachment = GPU_texture_framebuffer_attachment(tex); | ||||
| Context not available. | |||||
| attachment = GL_COLOR_ATTACHMENT0_EXT + fb_attachment; | attachment = GL_COLOR_ATTACHMENT0_EXT + fb_attachment; | ||||
| } | } | ||||
| glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, GPU_texture_target(tex), 0, 0); | glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, target, 0, 0); | ||||
| GPU_texture_framebuffer_set(tex, NULL, -1); | GPU_texture_framebuffer_set(tex, NULL, -1); | ||||
| } | } | ||||
| Context not available. | |||||
| GG.currentfb = fb->object; | GG.currentfb = fb->object; | ||||
| } | } | ||||
| void GPU_framebuffer_bind_simple(GPUFrameBuffer *fb) | |||||
| { | |||||
| glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); | |||||
| /* last bound prevails here, better allow explicit control here too */ | |||||
| glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); | |||||
| glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); | |||||
| GG.currentfb = fb->object; | |||||
| } | |||||
| bool GPU_framebuffer_bound(GPUFrameBuffer *fb) | bool GPU_framebuffer_bound(GPUFrameBuffer *fb) | ||||
| { | { | ||||
| return fb->object == GG.currentfb; | return fb->object == GG.currentfb; | ||||
| Context not available. | |||||
| return true; | return true; | ||||
| } | } | ||||
| int GPU_framebuffer_renderbuffer_attach(GPUFrameBuffer *fb, GPURenderBuffer *rb, int slot, char err_out[256]) | |||||
| { | |||||
| GLenum attachement; | |||||
| GLenum error; | |||||
| if (slot >= GPU_FB_MAX_SLOTS) { | |||||
| fprintf(stderr, | |||||
| "Attaching to index %d framebuffer slot unsupported. " | |||||
| "Use at most %d\n", slot, GPU_FB_MAX_SLOTS); | |||||
| return 0; | |||||
| } | |||||
| if (GPU_renderbuffer_depth(rb)) { | |||||
| attachement = GL_DEPTH_ATTACHMENT_EXT; | |||||
| } | |||||
| else { | |||||
| attachement = GL_COLOR_ATTACHMENT0_EXT + slot; | |||||
| } | |||||
| glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); | |||||
| GG.currentfb = fb->object; | |||||
| /* Clean glError buffer. */ | |||||
| while (glGetError() != GL_NO_ERROR) {} | |||||
| glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachement, GL_RENDERBUFFER_EXT, GPU_renderbuffer_bindcode(rb)); | |||||
| error = glGetError(); | |||||
| if (error == GL_INVALID_OPERATION) { | |||||
| GPU_framebuffer_restore(); | |||||
| GPU_print_framebuffer_error(error, err_out); | |||||
| return 0; | |||||
| } | |||||
| if (GPU_renderbuffer_depth(rb)) | |||||
| fb->depthrb = rb; | |||||
| else | |||||
| fb->colorrb[slot] = rb; | |||||
| GPU_renderbuffer_framebuffer_set(rb, fb, slot); | |||||
| return 1; | |||||
| } | |||||
| void GPU_framebuffer_renderbuffer_detach(GPURenderBuffer *rb) | |||||
| { | |||||
| GLenum attachment; | |||||
| GPUFrameBuffer *fb = GPU_renderbuffer_framebuffer(rb); | |||||
| int fb_attachment = GPU_renderbuffer_framebuffer_attachment(rb); | |||||
| if (!fb) | |||||
| return; | |||||
| if (GG.currentfb != fb->object) { | |||||
| glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); | |||||
| GG.currentfb = fb->object; | |||||
| } | |||||
| if (GPU_renderbuffer_depth(rb)) { | |||||
| fb->depthrb = NULL; | |||||
| attachment = GL_DEPTH_ATTACHMENT_EXT; | |||||
| } | |||||
| else { | |||||
| BLI_assert(fb->colorrb[fb_attachment] == rb); | |||||
| fb->colorrb[fb_attachment] = NULL; | |||||
| attachment = GL_COLOR_ATTACHMENT0_EXT + fb_attachment; | |||||
| } | |||||
| glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, 0); | |||||
| GPU_renderbuffer_framebuffer_set(rb, NULL, -1); | |||||
| } | |||||
| void GPU_framebuffer_free(GPUFrameBuffer *fb) | void GPU_framebuffer_free(GPUFrameBuffer *fb) | ||||
| { | { | ||||
| int i; | int i; | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| if (fb->depthrb) | |||||
| GPU_framebuffer_renderbuffer_detach(fb->depthrb); | |||||
| for (i = 0; i < GPU_FB_MAX_SLOTS; i++) { | |||||
| if (fb->colorrb[i]) { | |||||
| GPU_framebuffer_renderbuffer_detach(fb->colorrb[i]); | |||||
| } | |||||
| } | |||||
| if (fb->object) { | if (fb->object) { | ||||
| glDeleteFramebuffersEXT(1, &fb->object); | glDeleteFramebuffersEXT(1, &fb->object); | ||||
| Context not available. | |||||
| void GPU_framebuffer_blur( | void GPU_framebuffer_blur( | ||||
| GPUFrameBuffer *fb, GPUTexture *tex, | GPUFrameBuffer *fb, GPUTexture *tex, | ||||
| GPUFrameBuffer *blurfb, GPUTexture *blurtex) | GPUFrameBuffer *blurfb, GPUTexture *blurtex, float sharpness) | ||||
| { | { | ||||
| const float scaleh[2] = {1.0f / GPU_texture_width(blurtex), 0.0f}; | const float scaleh[2] = {(1.0f - sharpness) / GPU_texture_width(blurtex), 0.0f}; | ||||
| const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(tex)}; | const float scalev[2] = {0.0f, (1.0f - sharpness) / GPU_texture_height(tex)}; | ||||
| GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR); | GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR); | ||||
| int scale_uniform, texture_source_uniform; | int scale_uniform, texture_source_uniform; | ||||
| Context not available. | |||||
| glTexCoord2d(0, 1); glVertex2f(1, -1); | glTexCoord2d(0, 1); glVertex2f(1, -1); | ||||
| glEnd(); | glEnd(); | ||||
| GPU_texture_unbind(blurtex); | |||||
| GPU_shader_unbind(); | GPU_shader_unbind(); | ||||
| } | } | ||||
| /* GPURenderBuffer */ | |||||
| struct GPURenderBuffer { | |||||
| int width; | |||||
| int height; | |||||
| int samples; | |||||
| GPUFrameBuffer *fb; /* GPUFramebuffer this render buffer is attached to */ | |||||
| int fb_attachment; /* slot the render buffer is attached to */ | |||||
| bool depth; | |||||
| unsigned int bindcode; | |||||
| }; | |||||
| GPURenderBuffer *GPU_renderbuffer_create(int width, int height, int samples, GPUHDRType hdrtype, GPURenderBufferType type, char err_out[256]) | |||||
| { | |||||
| GPURenderBuffer *rb = MEM_callocN(sizeof(GPURenderBuffer), "GPURenderBuffer"); | |||||
| glGenRenderbuffers(1, &rb->bindcode); | |||||
| if (!rb->bindcode) { | |||||
| if (err_out) { | |||||
| BLI_snprintf(err_out, 256, "GPURenderBuffer: render buffer creation failed: %d", | |||||
| (int)glGetError()); | |||||
| } | |||||
| else { | |||||
| fprintf(stderr, "GPURenderBuffer: render buffer creation failed: %d\n", | |||||
| (int)glGetError()); | |||||
| } | |||||
| GPU_renderbuffer_free(rb); | |||||
| return NULL; | |||||
| } | |||||
| rb->width = width; | |||||
| rb->height = height; | |||||
| rb->samples = samples; | |||||
| glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rb->bindcode); | |||||
| if (type == GPU_RENDERBUFFER_DEPTH) { | |||||
| if (samples > 0) { | |||||
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, GL_DEPTH_COMPONENT, width, height); | |||||
| } | |||||
| else { | |||||
| glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height); | |||||
| } | |||||
| rb->depth = true; | |||||
| } | |||||
| else { | |||||
| GLenum internalformat = GL_RGBA8; | |||||
| switch (hdrtype) { | |||||
| case GPU_HDR_NONE: | |||||
| { | |||||
| internalformat = GL_RGBA8; | |||||
| break; | |||||
| } | |||||
| /* the following formats rely on ARB_texture_float or OpenGL 3.0 */ | |||||
| case GPU_HDR_HALF_FLOAT: | |||||
| { | |||||
| internalformat = GL_RGBA16F_ARB; | |||||
| break; | |||||
| } | |||||
| case GPU_HDR_FULL_FLOAT: | |||||
| { | |||||
| internalformat = GL_RGBA32F_ARB; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (samples > 0) { | |||||
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, internalformat, width, height); | |||||
| } | |||||
| else { | |||||
| glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalformat, width, height); | |||||
| } | |||||
| } | |||||
| glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); | |||||
| return rb; | |||||
| } | |||||
| void GPU_renderbuffer_free(GPURenderBuffer *rb) | |||||
| { | |||||
| if (rb->bindcode) { | |||||
| glDeleteRenderbuffersEXT(1, &rb->bindcode); | |||||
| } | |||||
| MEM_freeN(rb); | |||||
| } | |||||
| GPUFrameBuffer *GPU_renderbuffer_framebuffer(GPURenderBuffer *rb) | |||||
| { | |||||
| return rb->fb; | |||||
| } | |||||
| int GPU_renderbuffer_framebuffer_attachment(GPURenderBuffer *rb) | |||||
| { | |||||
| return rb->fb_attachment; | |||||
| } | |||||
| void GPU_renderbuffer_framebuffer_set(GPURenderBuffer *rb, GPUFrameBuffer *fb, int attachement) | |||||
| { | |||||
| rb->fb = fb; | |||||
| rb->fb_attachment = attachement; | |||||
| } | |||||
| int GPU_renderbuffer_bindcode(const GPURenderBuffer *rb) | |||||
| { | |||||
| return rb->bindcode; | |||||
| } | |||||
| bool GPU_renderbuffer_depth(const GPURenderBuffer *rb) | |||||
| { | |||||
| return rb->depth; | |||||
| } | |||||
| int GPU_renderbuffer_width(const GPURenderBuffer *rb) | |||||
| { | |||||
| return rb->width; | |||||
| } | |||||
| int GPU_renderbuffer_height(const GPURenderBuffer *rb) | |||||
| { | |||||
| return rb->height; | |||||
| } | |||||
| /* GPUOffScreen */ | /* GPUOffScreen */ | ||||
| struct GPUOffScreen { | struct GPUOffScreen { | ||||
| GPUFrameBuffer *fb; | GPUFrameBuffer *fb; | ||||
| GPUTexture *color; | GPUTexture *color; | ||||
| GPUTexture *depth; | GPUTexture *depth; | ||||
| GPURenderBuffer *rbcolor; | |||||
| GPURenderBuffer *rbdepth; | |||||
| int samples; | |||||
| }; | }; | ||||
| GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256]) | GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, GPUHDRType hdrtype, int mode, char err_out[256]) | ||||
| { | { | ||||
| GPUOffScreen *ofs; | GPUOffScreen *ofs; | ||||
| Context not available. | |||||
| if (samples) { | if (samples) { | ||||
| if (!GLEW_EXT_framebuffer_multisample || | if (!GLEW_EXT_framebuffer_multisample || | ||||
| !GLEW_ARB_texture_multisample || | /* Disable multisample for texture and not render buffers | ||||
| * when it's not supported */ | |||||
| (!GLEW_ARB_texture_multisample && (!(mode & GPU_OFFSCREEN_RENDERBUFFER_COLOR) || !(mode & GPU_OFFSCREEN_RENDERBUFFER_DEPTH))) || | |||||
| /* Only needed for GPU_offscreen_read_pixels. | /* Only needed for GPU_offscreen_read_pixels. | ||||
| * We could add an arg if we intend to use multi-sample | * We could add an arg if we intend to use multi-sample | ||||
| * offscreen buffers w/o reading their pixels */ | * offscreen buffers w/o reading their pixels */ | ||||
| !GLEW_EXT_framebuffer_blit || | !GLEW_EXT_framebuffer_blit | ||||
| /* Some GPUs works even without this extension. */ | |||||
| #if 0 | |||||
| /* This is required when blitting from a multi-sampled buffers, | /* This is required when blitting from a multi-sampled buffers, | ||||
| * even though we're not scaling. */ | * even though we're not scaling. */ | ||||
| !GLEW_EXT_framebuffer_multisample_blit_scaled) | || !GLEW_EXT_framebuffer_multisample_blit_scaled | ||||
| #endif | |||||
| ) | |||||
| { | { | ||||
| samples = 0; | samples = 0; | ||||
| } | } | ||||
| } | } | ||||
| ofs->depth = GPU_texture_create_depth_multisample(width, height, samples, err_out); | ofs->samples = samples; | ||||
| if (!ofs->depth) { | |||||
| GPU_offscreen_free(ofs); | if (mode & GPU_OFFSCREEN_RENDERBUFFER_COLOR) { | ||||
| return NULL; | ofs->rbcolor = GPU_renderbuffer_create(width, height, samples, hdrtype, GPU_RENDERBUFFER_COLOR, err_out); | ||||
| if (!ofs->rbcolor) { | |||||
| GPU_offscreen_free(ofs); | |||||
| return NULL; | |||||
| } | |||||
| if (!GPU_framebuffer_renderbuffer_attach(ofs->fb, ofs->rbcolor, 0, err_out)) { | |||||
| GPU_offscreen_free(ofs); | |||||
| return NULL; | |||||
| } | |||||
| } | } | ||||
| else { | |||||
| ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, hdrtype, samples, err_out); | |||||
| if (!ofs->color) { | |||||
| GPU_offscreen_free(ofs); | |||||
| return NULL; | |||||
| } | |||||
| if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) { | if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) { | ||||
| GPU_offscreen_free(ofs); | GPU_offscreen_free(ofs); | ||||
| return NULL; | return NULL; | ||||
| } | |||||
| } | } | ||||
| ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, GPU_HDR_NONE, samples, err_out); | if (mode & GPU_OFFSCREEN_RENDERBUFFER_DEPTH) { | ||||
| if (!ofs->color) { | ofs->rbdepth = GPU_renderbuffer_create(width, height, samples, GPU_HDR_NONE, GPU_RENDERBUFFER_DEPTH, err_out); | ||||
| GPU_offscreen_free(ofs); | if (!ofs->rbdepth) { | ||||
| return NULL; | GPU_offscreen_free(ofs); | ||||
| return NULL; | |||||
| } | |||||
| if (!GPU_framebuffer_renderbuffer_attach(ofs->fb, ofs->rbdepth, 0, err_out)) { | |||||
| GPU_offscreen_free(ofs); | |||||
| return NULL; | |||||
| } | |||||
| } | } | ||||
| else { | |||||
| ofs->depth = GPU_texture_create_depth_multisample(width, height, samples, (mode & GPU_OFFSCREEN_DEPTH_COMPARE), err_out); | |||||
| if (!ofs->depth) { | |||||
| GPU_offscreen_free(ofs); | |||||
| return NULL; | |||||
| } | |||||
| if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) { | if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) { | ||||
| GPU_offscreen_free(ofs); | GPU_offscreen_free(ofs); | ||||
| return NULL; | return NULL; | ||||
| } | |||||
| } | } | ||||
| /* check validity at the very end! */ | /* check validity at the very end! */ | ||||
| if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) { | if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) { | ||||
| GPU_offscreen_free(ofs); | GPU_offscreen_free(ofs); | ||||
| Context not available. | |||||
| GPU_texture_free(ofs->color); | GPU_texture_free(ofs->color); | ||||
| if (ofs->depth) | if (ofs->depth) | ||||
| GPU_texture_free(ofs->depth); | GPU_texture_free(ofs->depth); | ||||
| if (ofs->rbcolor) { | |||||
| GPU_renderbuffer_free(ofs->rbcolor); | |||||
| } | |||||
| if (ofs->rbdepth) { | |||||
| GPU_renderbuffer_free(ofs->rbdepth); | |||||
| } | |||||
| MEM_freeN(ofs); | MEM_freeN(ofs); | ||||
| } | } | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| void GPU_offscreen_bind_simple(GPUOffScreen *ofs) | |||||
| { | |||||
| GPU_framebuffer_bind_simple(ofs->fb); | |||||
| } | |||||
| void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) | void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) | ||||
| { | { | ||||
| if (restore) | if (restore) | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| void GPU_offscreen_blit(GPUOffScreen *srcofs, GPUOffScreen *dstofs, bool color, bool depth) | |||||
| { | |||||
| BLI_assert(color || depth); | |||||
| glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcofs->fb->object); | |||||
| glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstofs->fb->object); | |||||
| glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); | |||||
| glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); | |||||
| int height = min_ff(GPU_offscreen_height(srcofs), GPU_offscreen_height(dstofs)); | |||||
| int width = min_ff(GPU_offscreen_width(srcofs), GPU_offscreen_width(dstofs)); | |||||
| int mask = 0; | |||||
| if (color) { | |||||
| mask |= GL_COLOR_BUFFER_BIT; | |||||
| } | |||||
| if (depth) { | |||||
| mask |= GL_DEPTH_BUFFER_BIT; | |||||
| } | |||||
| glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height, mask, GL_NEAREST); | |||||
| // Call GPU_framebuffer_bind_simple to change GG.currentfb. | |||||
| GPU_framebuffer_bind_simple(dstofs->fb); | |||||
| } | |||||
| int GPU_offscreen_width(const GPUOffScreen *ofs) | int GPU_offscreen_width(const GPUOffScreen *ofs) | ||||
| { | { | ||||
| return GPU_texture_width(ofs->color); | if (ofs->color) { | ||||
| return GPU_texture_width(ofs->color); | |||||
| } | |||||
| else if (ofs->rbcolor) { | |||||
| return GPU_renderbuffer_width(ofs->rbcolor); | |||||
| } | |||||
| // Should never happen. | |||||
| return 0; | |||||
| } | } | ||||
| int GPU_offscreen_height(const GPUOffScreen *ofs) | int GPU_offscreen_height(const GPUOffScreen *ofs) | ||||
| { | { | ||||
| return GPU_texture_height(ofs->color); | if (ofs->color) { | ||||
| return GPU_texture_height(ofs->color); | |||||
| } | |||||
| else if (ofs->rbcolor) { | |||||
| return GPU_renderbuffer_height(ofs->rbcolor); | |||||
| } | |||||
| // Should never happen. | |||||
| return 0; | |||||
| } | |||||
| int GPU_offscreen_samples(const GPUOffScreen *ofs) | |||||
| { | |||||
| return ofs->samples; | |||||
| } | } | ||||
| int GPU_offscreen_color_texture(const GPUOffScreen *ofs) | int GPU_offscreen_color_texture(const GPUOffScreen *ofs) | ||||
| Context not available. | |||||
| return GPU_texture_opengl_bindcode(ofs->color); | return GPU_texture_opengl_bindcode(ofs->color); | ||||
| } | } | ||||
| GPUTexture *GPU_offscreen_texture(const GPUOffScreen *ofs) | |||||
| { | |||||
| return ofs->color; | |||||
| } | |||||
| GPUTexture *GPU_offscreen_depth_texture(const GPUOffScreen *ofs) | |||||
| { | |||||
| return ofs->depth; | |||||
| } | |||||
| Context not available. | |||||