| /************************************************************************** |
| * |
| * Copyright (C) 2014 Red Hat Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| **************************************************************************/ |
| |
| /* gallium blitter implementation in GL */ |
| /* for when we can't use glBlitFramebuffer */ |
| #include <epoxy/gl.h> |
| |
| #include <stdio.h> |
| #include "pipe/p_shader_tokens.h" |
| |
| #include "pipe/p_context.h" |
| #include "pipe/p_defines.h" |
| #include "pipe/p_screen.h" |
| #include "pipe/p_state.h" |
| #include "util/u_inlines.h" |
| #include "util/u_memory.h" |
| #include "util/u_dual_blend.h" |
| |
| #include "util/u_double_list.h" |
| #include "util/u_format.h" |
| #include "util/u_texture.h" |
| #include "tgsi/tgsi_parse.h" |
| |
| #include "vrend_object.h" |
| #include "vrend_shader.h" |
| |
| #include "vrend_renderer.h" |
| |
| #include "vrend_blitter.h" |
| |
| #define DEST_SWIZZLE_SNIPPET_SIZE 64 |
| |
| struct vrend_blitter_ctx { |
| virgl_gl_context gl_context; |
| bool initialised; |
| bool use_gles; |
| int framebuffer_srgb_enabled; |
| |
| GLuint vaoid; |
| |
| GLuint vs; |
| GLuint vs_pos_only; |
| GLuint fs_texfetch_col[PIPE_MAX_TEXTURE_TYPES]; |
| GLuint fs_texfetch_depth[PIPE_MAX_TEXTURE_TYPES]; |
| GLuint fs_texfetch_depth_msaa[PIPE_MAX_TEXTURE_TYPES]; |
| GLuint fs_texfetch_col_swizzle; |
| GLuint fb_id; |
| |
| unsigned dst_width; |
| unsigned dst_height; |
| |
| GLuint vbo_id; |
| GLfloat vertices[4][2][4]; /**< {pos, color} or {pos, texcoord} */ |
| }; |
| |
| static struct vrend_blitter_ctx vrend_blit_ctx; |
| |
| struct vrend_blitter_point { |
| int x; |
| int y; |
| }; |
| |
| struct vrend_blitter_delta { |
| int dx; |
| int dy; |
| }; |
| |
| static bool build_and_check(GLuint id, const char *buf) |
| { |
| GLint param; |
| glShaderSource(id, 1, (const char **)&buf, NULL); |
| glCompileShader(id); |
| |
| glGetShaderiv(id, GL_COMPILE_STATUS, ¶m); |
| if (param == GL_FALSE) { |
| char infolog[65536]; |
| int len; |
| glGetShaderInfoLog(id, 65536, &len, infolog); |
| fprintf(stderr,"shader failed to compile\n%s\n", infolog); |
| fprintf(stderr,"GLSL:\n%s\n", buf); |
| return false; |
| } |
| return true; |
| } |
| |
| static bool blit_build_vs_passthrough(struct vrend_blitter_ctx *blit_ctx) |
| { |
| blit_ctx->vs = glCreateShader(GL_VERTEX_SHADER); |
| |
| if (!build_and_check(blit_ctx->vs, |
| blit_ctx->use_gles ? VS_PASSTHROUGH_GLES : VS_PASSTHROUGH_GL)) { |
| glDeleteShader(blit_ctx->vs); |
| blit_ctx->vs = 0; |
| return false; |
| } |
| return true; |
| } |
| |
| static void create_dest_swizzle_snippet(const uint8_t swizzle[4], |
| char snippet[DEST_SWIZZLE_SNIPPET_SIZE]) |
| { |
| static const uint8_t invalid_swizzle = 0xff; |
| ssize_t si = 0; |
| uint8_t inverse[4] = {invalid_swizzle, invalid_swizzle, invalid_swizzle, |
| invalid_swizzle}; |
| |
| for (int i = 0; i < 4; ++i) { |
| if (swizzle[i] > 3) continue; |
| if (inverse[swizzle[i]] == invalid_swizzle) |
| inverse[swizzle[i]] = i; |
| } |
| |
| for (int i = 0; i < 4; ++i) { |
| int res = -1; |
| if (inverse[i] > 3) { |
| /* Use 0.0f for unused color values, 1.0f for an unused alpha value */ |
| res = snprintf(&snippet[si], DEST_SWIZZLE_SNIPPET_SIZE - si, |
| i < 3 ? "0.0f, " : "1.0f"); |
| } else { |
| res = snprintf(&snippet[si], DEST_SWIZZLE_SNIPPET_SIZE - si, |
| "texel.%c%s", "rgba"[inverse[i]], i < 3 ? ", " : ""); |
| } |
| si += res > 0 ? res : 0; |
| } |
| } |
| |
| static const char *vec4_type_for_tgsi_ret(enum tgsi_return_type tgsi_ret) |
| { |
| switch (tgsi_ret) { |
| case TGSI_RETURN_TYPE_SINT: return "ivec4"; |
| case TGSI_RETURN_TYPE_UINT: return "uvec4"; |
| default: return "vec4"; |
| } |
| } |
| |
| static enum tgsi_return_type tgsi_ret_for_format(enum virgl_formats format) |
| { |
| if (util_format_is_pure_uint(format)) |
| return TGSI_RETURN_TYPE_UINT; |
| else if (util_format_is_pure_sint(format)) |
| return TGSI_RETURN_TYPE_SINT; |
| |
| return TGSI_RETURN_TYPE_UNORM; |
| } |
| |
| static GLuint blit_build_frag_tex_col(struct vrend_blitter_ctx *blit_ctx, |
| int tgsi_tex_target, |
| enum tgsi_return_type tgsi_ret, |
| const uint8_t swizzle[4]) |
| { |
| GLuint fs_id; |
| char shader_buf[4096]; |
| int is_shad; |
| const char *twm; |
| const char *ext_str = ""; |
| char dest_swizzle_snippet[DEST_SWIZZLE_SNIPPET_SIZE] = "texel"; |
| switch (tgsi_tex_target) { |
| case TGSI_TEXTURE_1D: |
| case TGSI_TEXTURE_BUFFER: |
| twm = ".x"; |
| break; |
| case TGSI_TEXTURE_1D_ARRAY: |
| case TGSI_TEXTURE_2D: |
| case TGSI_TEXTURE_RECT: |
| case TGSI_TEXTURE_2D_MSAA: |
| default: |
| twm = ".xy"; |
| break; |
| case TGSI_TEXTURE_SHADOW1D: |
| case TGSI_TEXTURE_SHADOW2D: |
| case TGSI_TEXTURE_SHADOW1D_ARRAY: |
| case TGSI_TEXTURE_SHADOWRECT: |
| case TGSI_TEXTURE_3D: |
| case TGSI_TEXTURE_CUBE: |
| case TGSI_TEXTURE_2D_ARRAY: |
| case TGSI_TEXTURE_2D_ARRAY_MSAA: |
| twm = ".xyz"; |
| break; |
| case TGSI_TEXTURE_SHADOWCUBE: |
| case TGSI_TEXTURE_SHADOW2D_ARRAY: |
| case TGSI_TEXTURE_SHADOWCUBE_ARRAY: |
| case TGSI_TEXTURE_CUBE_ARRAY: |
| twm = ""; |
| break; |
| } |
| |
| if (tgsi_tex_target == TGSI_TEXTURE_CUBE_ARRAY || |
| tgsi_tex_target == TGSI_TEXTURE_SHADOWCUBE_ARRAY) |
| ext_str = "#extension GL_ARB_texture_cube_map_array : require\n"; |
| |
| if (swizzle) |
| create_dest_swizzle_snippet(swizzle, dest_swizzle_snippet); |
| |
| snprintf(shader_buf, 4096, blit_ctx->use_gles ? FS_TEXFETCH_COL_GLES : FS_TEXFETCH_COL_GL, |
| ext_str, vec4_type_for_tgsi_ret(tgsi_ret), |
| vrend_shader_samplerreturnconv(tgsi_ret), |
| vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), twm, |
| dest_swizzle_snippet); |
| |
| fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| if (!build_and_check(fs_id, shader_buf)) { |
| glDeleteShader(fs_id); |
| return 0; |
| } |
| |
| return fs_id; |
| } |
| |
| static GLuint blit_build_frag_tex_col_msaa(struct vrend_blitter_ctx *blit_ctx, |
| int tgsi_tex_target, |
| enum tgsi_return_type tgsi_ret, |
| const uint8_t swizzle[4], |
| int nr_samples) |
| { |
| GLuint fs_id; |
| char shader_buf[4096]; |
| int is_shad; |
| const char *twm; |
| const char *ivec; |
| char dest_swizzle_snippet[DEST_SWIZZLE_SNIPPET_SIZE] = "texel"; |
| const char *ext_str = blit_ctx->use_gles ? "" : |
| "#extension GL_ARB_texture_multisample : enable\n"; |
| |
| switch (tgsi_tex_target) { |
| case TGSI_TEXTURE_2D_MSAA: |
| twm = ".xy"; |
| ivec = "ivec2"; |
| break; |
| case TGSI_TEXTURE_2D_ARRAY_MSAA: |
| twm = ".xyz"; |
| ivec = "ivec3"; |
| break; |
| default: |
| return 0; |
| } |
| |
| if (swizzle) |
| create_dest_swizzle_snippet(swizzle, dest_swizzle_snippet); |
| |
| snprintf(shader_buf, 4096, |
| blit_ctx->use_gles ? FS_TEXFETCH_COL_MSAA_GLES : FS_TEXFETCH_COL_MSAA_GL, |
| ext_str, vec4_type_for_tgsi_ret(tgsi_ret), |
| vrend_shader_samplerreturnconv(tgsi_ret), |
| vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), |
| nr_samples, ivec, twm, dest_swizzle_snippet); |
| |
| fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| if (!build_and_check(fs_id, shader_buf)) { |
| glDeleteShader(fs_id); |
| return 0; |
| } |
| |
| return fs_id; |
| } |
| |
| static GLuint blit_build_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target) |
| { |
| GLuint fs_id; |
| char shader_buf[4096]; |
| int is_shad; |
| const char *twm; |
| |
| switch (tgsi_tex_target) { |
| case TGSI_TEXTURE_1D: |
| case TGSI_TEXTURE_BUFFER: |
| twm = ".x"; |
| break; |
| case TGSI_TEXTURE_1D_ARRAY: |
| case TGSI_TEXTURE_2D: |
| case TGSI_TEXTURE_RECT: |
| case TGSI_TEXTURE_2D_MSAA: |
| default: |
| twm = ".xy"; |
| break; |
| case TGSI_TEXTURE_SHADOW1D: |
| case TGSI_TEXTURE_SHADOW2D: |
| case TGSI_TEXTURE_SHADOW1D_ARRAY: |
| case TGSI_TEXTURE_SHADOWRECT: |
| case TGSI_TEXTURE_3D: |
| case TGSI_TEXTURE_CUBE: |
| case TGSI_TEXTURE_2D_ARRAY: |
| case TGSI_TEXTURE_2D_ARRAY_MSAA: |
| twm = ".xyz"; |
| break; |
| case TGSI_TEXTURE_SHADOWCUBE: |
| case TGSI_TEXTURE_SHADOW2D_ARRAY: |
| case TGSI_TEXTURE_SHADOWCUBE_ARRAY: |
| case TGSI_TEXTURE_CUBE_ARRAY: |
| twm = ""; |
| break; |
| } |
| |
| snprintf(shader_buf, 4096, blit_ctx->use_gles ? FS_TEXFETCH_DS_GLES : FS_TEXFETCH_DS_GL, |
| vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), twm); |
| |
| fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| if (!build_and_check(fs_id, shader_buf)) { |
| glDeleteShader(fs_id); |
| return 0; |
| } |
| |
| return fs_id; |
| } |
| |
| static GLuint blit_build_frag_blit_msaa_depth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target) |
| { |
| GLuint fs_id; |
| char shader_buf[4096]; |
| int is_shad; |
| const char *twm; |
| const char *ivec; |
| |
| switch (tgsi_tex_target) { |
| case TGSI_TEXTURE_2D_MSAA: |
| twm = ".xy"; |
| ivec = "ivec2"; |
| break; |
| case TGSI_TEXTURE_2D_ARRAY_MSAA: |
| twm = ".xyz"; |
| ivec = "ivec3"; |
| break; |
| default: |
| return 0; |
| } |
| |
| snprintf(shader_buf, 4096, blit_ctx->use_gles ? FS_TEXFETCH_DS_MSAA_GLES : FS_TEXFETCH_DS_MSAA_GL, |
| vrend_shader_samplertypeconv(tgsi_tex_target, &is_shad), ivec, twm); |
| |
| fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| if (!build_and_check(fs_id, shader_buf)) { |
| glDeleteShader(fs_id); |
| return 0; |
| } |
| |
| return fs_id; |
| } |
| |
| static GLuint blit_get_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int pipe_tex_target, unsigned nr_samples) |
| { |
| assert(pipe_tex_target < PIPE_MAX_TEXTURE_TYPES); |
| |
| if (nr_samples > 1) { |
| GLuint *shader = &blit_ctx->fs_texfetch_depth_msaa[pipe_tex_target]; |
| |
| if (!*shader) { |
| unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, nr_samples); |
| |
| *shader = blit_build_frag_blit_msaa_depth(blit_ctx, tgsi_tex); |
| } |
| return *shader; |
| |
| } else { |
| GLuint *shader = &blit_ctx->fs_texfetch_depth[pipe_tex_target]; |
| |
| if (!*shader) { |
| unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, 0); |
| |
| *shader = blit_build_frag_tex_writedepth(blit_ctx, tgsi_tex); |
| } |
| return *shader; |
| } |
| } |
| |
| static GLuint blit_get_frag_tex_col(struct vrend_blitter_ctx *blit_ctx, |
| int pipe_tex_target, |
| unsigned nr_samples, |
| const struct vrend_format_table *src_entry, |
| const struct vrend_format_table *dst_entry) |
| { |
| assert(pipe_tex_target < PIPE_MAX_TEXTURE_TYPES); |
| |
| bool needs_swizzle = dst_entry->flags & VIRGL_BIND_NEED_SWIZZLE; |
| |
| if (needs_swizzle || nr_samples > 1) { |
| const uint8_t *swizzle = needs_swizzle ? dst_entry->swizzle : NULL; |
| GLuint *shader = &blit_ctx->fs_texfetch_col_swizzle; |
| if (shader) { |
| glDeleteShader(*shader); |
| } |
| |
| unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, nr_samples); |
| enum tgsi_return_type tgsi_ret = tgsi_ret_for_format(src_entry->format); |
| |
| if (nr_samples > 1) { |
| // Integer textures are resolved using just one sample |
| int msaa_samples = tgsi_ret == TGSI_RETURN_TYPE_UNORM ? nr_samples : 1; |
| *shader = blit_build_frag_tex_col_msaa(blit_ctx, tgsi_tex, tgsi_ret, |
| swizzle, msaa_samples); |
| } else { |
| *shader = blit_build_frag_tex_col(blit_ctx, tgsi_tex, tgsi_ret, swizzle); |
| } |
| |
| return *shader; |
| } else { |
| GLuint *shader = &blit_ctx->fs_texfetch_col[pipe_tex_target]; |
| |
| if (!*shader) { |
| unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, 0); |
| enum tgsi_return_type tgsi_ret = tgsi_ret_for_format(src_entry->format); |
| |
| *shader = blit_build_frag_tex_col(blit_ctx, tgsi_tex, tgsi_ret, NULL); |
| } |
| return *shader; |
| } |
| } |
| |
| static void vrend_renderer_init_blit_ctx(struct vrend_blitter_ctx *blit_ctx) |
| { |
| struct virgl_gl_ctx_param ctx_params; |
| int i; |
| if (blit_ctx->initialised) { |
| vrend_clicbs->make_current(0, blit_ctx->gl_context); |
| return; |
| } |
| |
| blit_ctx->initialised = true; |
| blit_ctx->use_gles = epoxy_is_desktop_gl() == 0; |
| ctx_params.shared = true; |
| for (uint32_t i = 0; i < ARRAY_SIZE(gl_versions); i++) { |
| ctx_params.major_ver = gl_versions[i].major; |
| ctx_params.minor_ver = gl_versions[i].minor; |
| |
| blit_ctx->gl_context = vrend_clicbs->create_gl_context(0, &ctx_params); |
| if (blit_ctx->gl_context) |
| break; |
| } |
| |
| vrend_clicbs->make_current(0, blit_ctx->gl_context); |
| glGenVertexArrays(1, &blit_ctx->vaoid); |
| glGenFramebuffers(1, &blit_ctx->fb_id); |
| |
| glGenBuffers(1, &blit_ctx->vbo_id); |
| blit_build_vs_passthrough(blit_ctx); |
| |
| for (i = 0; i < 4; i++) |
| blit_ctx->vertices[i][0][3] = 1; /*v.w*/ |
| glBindVertexArray(blit_ctx->vaoid); |
| glBindBuffer(GL_ARRAY_BUFFER, blit_ctx->vbo_id); |
| |
| if (!blit_ctx->use_gles) |
| glEnable(GL_FRAMEBUFFER_SRGB); |
| blit_ctx->framebuffer_srgb_enabled = true; |
| } |
| |
| static inline GLenum convert_mag_filter(unsigned int filter) |
| { |
| if (filter == PIPE_TEX_FILTER_NEAREST) |
| return GL_NEAREST; |
| return GL_LINEAR; |
| } |
| |
| static void blitter_set_dst_dim(struct vrend_blitter_ctx *blit_ctx, |
| unsigned width, unsigned height) |
| { |
| blit_ctx->dst_width = width; |
| blit_ctx->dst_height = height; |
| } |
| |
| static void blitter_set_rectangle(struct vrend_blitter_ctx *blit_ctx, |
| int x1, int y1, int x2, int y2, |
| float depth) |
| { |
| int i; |
| |
| /* set vertex positions */ |
| blit_ctx->vertices[0][0][0] = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v0.x*/ |
| blit_ctx->vertices[0][0][1] = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v0.y*/ |
| |
| blit_ctx->vertices[1][0][0] = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v1.x*/ |
| blit_ctx->vertices[1][0][1] = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v1.y*/ |
| |
| blit_ctx->vertices[2][0][0] = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v2.x*/ |
| blit_ctx->vertices[2][0][1] = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v2.y*/ |
| |
| blit_ctx->vertices[3][0][0] = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v3.x*/ |
| blit_ctx->vertices[3][0][1] = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v3.y*/ |
| |
| for (i = 0; i < 4; i++) |
| blit_ctx->vertices[i][0][2] = depth; /*z*/ |
| |
| glViewport(0, 0, blit_ctx->dst_width, blit_ctx->dst_height); |
| } |
| |
| static void get_texcoords(struct vrend_resource *src_res, |
| int src_level, |
| int x1, int y1, int x2, int y2, |
| float out[4]) |
| { |
| bool normalized = src_res->base.target != PIPE_TEXTURE_RECT && |
| src_res->base.nr_samples <= 1; |
| |
| if (normalized) { |
| out[0] = x1 / (float)u_minify(src_res->base.width0, src_level); |
| out[1] = y1 / (float)u_minify(src_res->base.height0, src_level); |
| out[2] = x2 / (float)u_minify(src_res->base.width0, src_level); |
| out[3] = y2 / (float)u_minify(src_res->base.height0, src_level); |
| } else { |
| out[0] = (float) x1; |
| out[1] = (float) y1; |
| out[2] = (float) x2; |
| out[3] = (float) y2; |
| } |
| } |
| static void set_texcoords_in_vertices(const float coord[4], |
| float *out, unsigned stride) |
| { |
| out[0] = coord[0]; /*t0.s*/ |
| out[1] = coord[1]; /*t0.t*/ |
| out += stride; |
| out[0] = coord[2]; /*t1.s*/ |
| out[1] = coord[1]; /*t1.t*/ |
| out += stride; |
| out[0] = coord[2]; /*t2.s*/ |
| out[1] = coord[3]; /*t2.t*/ |
| out += stride; |
| out[0] = coord[0]; /*t3.s*/ |
| out[1] = coord[3]; /*t3.t*/ |
| } |
| |
| static void blitter_set_texcoords(struct vrend_blitter_ctx *blit_ctx, |
| struct vrend_resource *src_res, |
| int level, |
| float layer, unsigned sample, |
| int x1, int y1, int x2, int y2) |
| { |
| float coord[4]; |
| float face_coord[4][2]; |
| int i; |
| get_texcoords(src_res, level, x1, y1, x2, y2, coord); |
| |
| if (src_res->base.target == PIPE_TEXTURE_CUBE || |
| src_res->base.target == PIPE_TEXTURE_CUBE_ARRAY) { |
| set_texcoords_in_vertices(coord, &face_coord[0][0], 2); |
| util_map_texcoords2d_onto_cubemap((unsigned)layer % 6, |
| /* pointer, stride in floats */ |
| &face_coord[0][0], 2, |
| &blit_ctx->vertices[0][1][0], 8, |
| FALSE); |
| } else { |
| set_texcoords_in_vertices(coord, &blit_ctx->vertices[0][1][0], 8); |
| } |
| |
| switch (src_res->base.target) { |
| case PIPE_TEXTURE_3D: |
| { |
| float r = layer / (float)u_minify(src_res->base.depth0, |
| level); |
| for (i = 0; i < 4; i++) |
| blit_ctx->vertices[i][1][2] = r; /*r*/ |
| } |
| break; |
| |
| case PIPE_TEXTURE_1D_ARRAY: |
| for (i = 0; i < 4; i++) |
| blit_ctx->vertices[i][1][1] = (float) layer; /*t*/ |
| break; |
| |
| case PIPE_TEXTURE_2D_ARRAY: |
| for (i = 0; i < 4; i++) { |
| blit_ctx->vertices[i][1][2] = (float) layer; /*r*/ |
| blit_ctx->vertices[i][1][3] = (float) sample; /*q*/ |
| } |
| break; |
| case PIPE_TEXTURE_CUBE_ARRAY: |
| for (i = 0; i < 4; i++) |
| blit_ctx->vertices[i][1][3] = (float) ((unsigned)layer / 6); /*w*/ |
| break; |
| case PIPE_TEXTURE_2D: |
| for (i = 0; i < 4; i++) { |
| blit_ctx->vertices[i][1][3] = (float) sample; /*r*/ |
| } |
| break; |
| default:; |
| } |
| } |
| #if 0 |
| static void set_dsa_keep_depth_stencil(void) |
| { |
| glDisable(GL_STENCIL_TEST); |
| glDisable(GL_DEPTH_TEST); |
| glDepthMask(GL_FALSE); |
| } |
| #endif |
| |
| static void set_dsa_write_depth_keep_stencil(void) |
| { |
| glDisable(GL_STENCIL_TEST); |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_ALWAYS); |
| glDepthMask(GL_TRUE); |
| } |
| |
| static inline GLenum to_gl_swizzle(int swizzle) |
| { |
| switch (swizzle) { |
| case PIPE_SWIZZLE_RED: return GL_RED; |
| case PIPE_SWIZZLE_GREEN: return GL_GREEN; |
| case PIPE_SWIZZLE_BLUE: return GL_BLUE; |
| case PIPE_SWIZZLE_ALPHA: return GL_ALPHA; |
| case PIPE_SWIZZLE_ZERO: return GL_ZERO; |
| case PIPE_SWIZZLE_ONE: return GL_ONE; |
| default: |
| assert(0); |
| return 0; |
| } |
| } |
| |
| /* Calculate the delta required to keep 'v' within [0, max] */ |
| static int calc_delta_for_bound(int v, int max) |
| { |
| int delta = 0; |
| |
| if (v < 0) |
| delta = -v; |
| else if (v > max) |
| delta = - (v - max); |
| |
| return delta; |
| } |
| |
| /* Calculate the deltas for the source blit region points in order to bound |
| * them within the source resource extents */ |
| static void calc_src_deltas_for_bounds(struct vrend_resource *src_res, |
| const struct pipe_blit_info *info, |
| struct vrend_blitter_delta *src0_delta, |
| struct vrend_blitter_delta *src1_delta) |
| { |
| int max_x = u_minify(src_res->base.width0, info->src.level) - 1; |
| int max_y = u_minify(src_res->base.height0, info->src.level) - 1; |
| |
| /* Whether the bounds for the coordinates of a point are inclusive or |
| * exclusive depends on the direction of the blit read. Adjust the max |
| * bounds accordingly, with an adjustment of 0 for inclusive, and 1 for |
| * exclusive. */ |
| int src0_x_excl = info->src.box.width < 0; |
| int src0_y_excl = info->src.box.height < 0; |
| |
| src0_delta->dx = calc_delta_for_bound(info->src.box.x, max_x + src0_x_excl); |
| src0_delta->dy = calc_delta_for_bound(info->src.box.y, max_y + src0_y_excl); |
| |
| src1_delta->dx = calc_delta_for_bound(info->src.box.x + info->src.box.width, |
| max_x + !src0_x_excl); |
| src1_delta->dy = calc_delta_for_bound(info->src.box.y + info->src.box.height, |
| max_y + !src0_y_excl); |
| } |
| |
| /* Calculate dst delta values to adjust the dst points for any changes in the |
| * src points */ |
| static void calc_dst_deltas_from_src(const struct pipe_blit_info *info, |
| const struct vrend_blitter_delta *src0_delta, |
| const struct vrend_blitter_delta *src1_delta, |
| struct vrend_blitter_delta *dst0_delta, |
| struct vrend_blitter_delta *dst1_delta) |
| { |
| float scale_x = (float)info->dst.box.width / (float)info->src.box.width; |
| float scale_y = (float)info->dst.box.height / (float)info->src.box.height; |
| |
| dst0_delta->dx = src0_delta->dx * scale_x; |
| dst0_delta->dy = src0_delta->dy * scale_y; |
| |
| dst1_delta->dx = src1_delta->dx * scale_x; |
| dst1_delta->dy = src1_delta->dy * scale_y; |
| } |
| |
| /* implement blitting using OpenGL. */ |
| void vrend_renderer_blit_gl(UNUSED struct vrend_context *ctx, |
| struct vrend_resource *src_res, |
| struct vrend_resource *dst_res, |
| const struct pipe_blit_info *info, |
| bool has_texture_srgb_decode) |
| { |
| struct vrend_blitter_ctx *blit_ctx = &vrend_blit_ctx; |
| GLuint buffers; |
| GLuint prog_id; |
| GLuint fs_id; |
| GLint lret; |
| GLenum filter; |
| GLuint pos_loc, tc_loc; |
| GLuint samp_loc; |
| bool has_depth, has_stencil; |
| bool blit_stencil, blit_depth; |
| int dst_z; |
| struct vrend_blitter_delta src0_delta, src1_delta, dst0_delta, dst1_delta; |
| struct vrend_blitter_point src0, src1, dst0, dst1; |
| const struct util_format_description *src_desc = |
| util_format_description(src_res->base.format); |
| const struct util_format_description *dst_desc = |
| util_format_description(dst_res->base.format); |
| const struct vrend_format_table *src_entry = |
| vrend_get_format_table_entry(info->src.format); |
| const struct vrend_format_table *dst_entry = |
| vrend_get_format_table_entry(info->dst.format); |
| |
| has_depth = util_format_has_depth(src_desc) && |
| util_format_has_depth(dst_desc); |
| has_stencil = util_format_has_stencil(src_desc) && |
| util_format_has_stencil(dst_desc); |
| |
| blit_depth = has_depth && (info->mask & PIPE_MASK_Z); |
| blit_stencil = has_stencil && (info->mask & PIPE_MASK_S) & 0; |
| |
| filter = convert_mag_filter(info->filter); |
| vrend_renderer_init_blit_ctx(blit_ctx); |
| |
| blitter_set_dst_dim(blit_ctx, |
| u_minify(dst_res->base.width0, info->dst.level), |
| u_minify(dst_res->base.height0, info->dst.level)); |
| |
| /* Calculate src and dst points taking deltas into account */ |
| calc_src_deltas_for_bounds(src_res, info, &src0_delta, &src1_delta); |
| calc_dst_deltas_from_src(info, &src0_delta, &src1_delta, &dst0_delta, &dst1_delta); |
| |
| src0.x = info->src.box.x + src0_delta.dx; |
| src0.y = info->src.box.y + src0_delta.dy; |
| src1.x = info->src.box.x + info->src.box.width + src1_delta.dx; |
| src1.y = info->src.box.y + info->src.box.height + src1_delta.dy; |
| |
| dst0.x = info->dst.box.x + dst0_delta.dx; |
| dst0.y = info->dst.box.y + dst0_delta.dy; |
| dst1.x = info->dst.box.x + info->dst.box.width + dst1_delta.dx; |
| dst1.y = info->dst.box.y + info->dst.box.height + dst1_delta.dy; |
| |
| blitter_set_rectangle(blit_ctx, dst0.x, dst0.y, dst1.x, dst1.y, 0); |
| |
| prog_id = glCreateProgram(); |
| glAttachShader(prog_id, blit_ctx->vs); |
| |
| if (blit_depth || blit_stencil) { |
| fs_id = blit_get_frag_tex_writedepth(blit_ctx, src_res->base.target, |
| src_res->base.nr_samples); |
| } else { |
| fs_id = blit_get_frag_tex_col(blit_ctx, src_res->base.target, |
| src_res->base.nr_samples, |
| src_entry, dst_entry); |
| } |
| glAttachShader(prog_id, fs_id); |
| |
| glLinkProgram(prog_id); |
| glGetProgramiv(prog_id, GL_LINK_STATUS, &lret); |
| if (lret == GL_FALSE) { |
| char infolog[65536]; |
| int len; |
| glGetProgramInfoLog(prog_id, 65536, &len, infolog); |
| fprintf(stderr,"got error linking\n%s\n", infolog); |
| /* dump shaders */ |
| glDeleteProgram(prog_id); |
| return; |
| } |
| |
| glUseProgram(prog_id); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, blit_ctx->fb_id); |
| vrend_fb_bind_texture(dst_res, 0, info->dst.level, info->dst.box.z); |
| |
| buffers = GL_COLOR_ATTACHMENT0_EXT; |
| glDrawBuffers(1, &buffers); |
| |
| glBindTexture(src_res->target, src_res->id); |
| |
| if (src_entry->flags & VIRGL_BIND_NEED_SWIZZLE) { |
| glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_R, |
| to_gl_swizzle(src_entry->swizzle[0])); |
| glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_G, |
| to_gl_swizzle(src_entry->swizzle[1])); |
| glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_B, |
| to_gl_swizzle(src_entry->swizzle[2])); |
| glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_A, |
| to_gl_swizzle(src_entry->swizzle[3])); |
| } |
| |
| /* Just make sure that no stale state disabled decoding */ |
| if (has_texture_srgb_decode && util_format_is_srgb(src_res->base.format)) |
| glTexParameteri(src_res->target, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT); |
| |
| glTexParameteri(src_res->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(src_res->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(src_res->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); |
| |
| glTexParameteri(src_res->target, GL_TEXTURE_BASE_LEVEL, info->src.level); |
| glTexParameteri(src_res->target, GL_TEXTURE_MAX_LEVEL, info->src.level); |
| glTexParameterf(src_res->target, GL_TEXTURE_MAG_FILTER, filter); |
| glTexParameterf(src_res->target, GL_TEXTURE_MIN_FILTER, filter); |
| pos_loc = glGetAttribLocation(prog_id, "arg0"); |
| tc_loc = glGetAttribLocation(prog_id, "arg1"); |
| samp_loc = glGetUniformLocation(prog_id, "samp"); |
| |
| glUniform1i(samp_loc, 0); |
| |
| glVertexAttribPointer(pos_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0); |
| glVertexAttribPointer(tc_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(4 * sizeof(float))); |
| |
| glEnableVertexAttribArray(pos_loc); |
| glEnableVertexAttribArray(tc_loc); |
| |
| set_dsa_write_depth_keep_stencil(); |
| |
| for (dst_z = 0; dst_z < info->dst.box.depth; dst_z++) { |
| float dst2src_scale = info->src.box.depth / (float)info->dst.box.depth; |
| float dst_offset = ((info->src.box.depth - 1) - |
| (info->dst.box.depth - 1) * dst2src_scale) * 0.5; |
| float src_z = (dst_z + dst_offset) * dst2src_scale; |
| uint32_t layer = (dst_res->target == GL_TEXTURE_CUBE_MAP) ? info->dst.box.z : dst_z; |
| |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, blit_ctx->fb_id); |
| vrend_fb_bind_texture(dst_res, 0, info->dst.level, layer); |
| |
| buffers = GL_COLOR_ATTACHMENT0_EXT; |
| glDrawBuffers(1, &buffers); |
| blitter_set_texcoords(blit_ctx, src_res, info->src.level, |
| info->src.box.z + src_z, 0, |
| src0.x, src0.y, src1.x, src1.y); |
| |
| glBufferData(GL_ARRAY_BUFFER, sizeof(blit_ctx->vertices), blit_ctx->vertices, GL_STATIC_DRAW); |
| glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
| } |
| glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, |
| GL_TEXTURE_2D, 0, 0); |
| glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, 0, 0); |
| } |