X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fstate_tracker%2Fst_cb_drawpixels.c;h=2a1ac8f4f0269df4ab47c4e94d5720eaacb32c14;hb=caa98246a0e180a96f3fcdcd3bfcbef0b136bc11;hp=384f965474ade1b49354015400360c9abde471cc;hpb=68a0e15f44cf351eab97a734b1cffeade1873fdf;p=mesa.git diff --git a/src/mesa/state_tracker/st_cb_drawpixels.c b/src/mesa/state_tracker/st_cb_drawpixels.c index 384f965474a..2a1ac8f4f02 100644 --- a/src/mesa/state_tracker/st_cb_drawpixels.c +++ b/src/mesa/state_tracker/st_cb_drawpixels.c @@ -1,8 +1,8 @@ /************************************************************************** - * + * * Copyright 2007 VMware, Inc. * All Rights Reserved. - * + * * 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 @@ -10,11 +10,11 @@ * distribute, sub license, 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 (including the * next paragraph) 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 NON-INFRINGEMENT. @@ -22,7 +22,7 @@ * 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. - * + * **************************************************************************/ /* @@ -30,7 +30,8 @@ * Brian Paul */ -#include "main/imports.h" +#include "main/errors.h" + #include "main/image.h" #include "main/bufferobj.h" #include "main/blit.h" @@ -64,16 +65,20 @@ #include "st_sampler_view.h" #include "st_scissor.h" #include "st_texture.h" +#include "st_util.h" +#include "st_nir.h" #include "pipe/p_context.h" #include "pipe/p_defines.h" #include "tgsi/tgsi_ureg.h" -#include "util/u_format.h" +#include "util/format/u_format.h" #include "util/u_inlines.h" #include "util/u_math.h" +#include "util/u_simple_shaders.h" #include "util/u_tile.h" #include "cso_cache/cso_context.h" +#include "compiler/nir/nir_builder.h" /** * We have a simple glDrawPixels cache to try to optimize the case where the @@ -104,34 +109,105 @@ */ #define USE_DRAWPIXELS_CACHE 1 +static nir_ssa_def * +sample_via_nir(nir_builder *b, nir_variable *texcoord, + const char *name, int sampler, enum glsl_base_type base_type, + nir_alu_type alu_type) +{ + const struct glsl_type *sampler2D = + glsl_sampler_type(GLSL_SAMPLER_DIM_2D, false, false, base_type); + + nir_variable *var = + nir_variable_create(b->shader, nir_var_uniform, sampler2D, name); + var->data.binding = sampler; + var->data.explicit_binding = true; + + nir_deref_instr *deref = nir_build_deref_var(b, var); + + nir_tex_instr *tex = nir_tex_instr_create(b->shader, 3); + tex->op = nir_texop_tex; + tex->sampler_dim = GLSL_SAMPLER_DIM_2D; + tex->coord_components = 2; + tex->dest_type = alu_type; + tex->src[0].src_type = nir_tex_src_texture_deref; + tex->src[0].src = nir_src_for_ssa(&deref->dest.ssa); + tex->src[1].src_type = nir_tex_src_sampler_deref; + tex->src[1].src = nir_src_for_ssa(&deref->dest.ssa); + tex->src[2].src_type = nir_tex_src_coord; + tex->src[2].src = + nir_src_for_ssa(nir_channels(b, nir_load_var(b, texcoord), + (1 << tex->coord_components) - 1)); + + nir_ssa_dest_init(&tex->instr, &tex->dest, 4, 32, NULL); + nir_builder_instr_insert(b, &tex->instr); + return nir_channel(b, &tex->dest.ssa, 0); +} + +static void * +make_drawpix_z_stencil_program_nir(struct st_context *st, + bool write_depth, + bool write_stencil) +{ + struct nir_builder b; + const nir_shader_compiler_options *options = + st->ctx->Const.ShaderCompilerOptions[MESA_SHADER_FRAGMENT].NirOptions; + + nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_FRAGMENT, options); + + nir_variable *texcoord = + nir_variable_create(b.shader, nir_var_shader_in, glsl_vec_type(2), + "texcoord"); + texcoord->data.location = VARYING_SLOT_TEX0; + + if (write_depth) { + nir_variable *out = + nir_variable_create(b.shader, nir_var_shader_out, glsl_float_type(), + "gl_FragDepth"); + out->data.location = FRAG_RESULT_DEPTH; + nir_ssa_def *depth = sample_via_nir(&b, texcoord, "depth", 0, + GLSL_TYPE_FLOAT, nir_type_float); + nir_store_var(&b, out, depth, 0x1); + + /* Also copy color */ + nir_variable *color_in = + nir_variable_create(b.shader, nir_var_shader_in, glsl_vec_type(4), + "v_color"); + color_in->data.location = VARYING_SLOT_COL0; + + nir_variable *color_out = + nir_variable_create(b.shader, nir_var_shader_out, glsl_vec_type(4), + "gl_FragColor"); + color_out->data.location = FRAG_RESULT_COLOR; + nir_copy_var(&b, color_out, color_in); + } + + if (write_stencil) { + nir_variable *out = + nir_variable_create(b.shader, nir_var_shader_out, glsl_uint_type(), + "gl_FragStencilRefARB"); + out->data.location = FRAG_RESULT_STENCIL; + nir_ssa_def *stencil = sample_via_nir(&b, texcoord, "stencil", 1, + GLSL_TYPE_UINT, nir_type_uint); + nir_store_var(&b, out, stencil, 0x1); + } + + char name[14]; + snprintf(name, 14, "drawpixels %s%s", + write_depth ? "Z" : "", write_stencil ? "S" : ""); + + return st_nir_finish_builtin_shader(st, b.shader, name); +} -/** - * Create fragment program that does a TEX() instruction to get a Z and/or - * stencil value value, then writes to FRAG_RESULT_DEPTH/FRAG_RESULT_STENCIL. - * Used for glDrawPixels(GL_DEPTH_COMPONENT / GL_STENCIL_INDEX). - * Pass fragment color through as-is. - * - * \return CSO of the fragment shader. - */ static void * -get_drawpix_z_stencil_program(struct st_context *st, - GLboolean write_depth, - GLboolean write_stencil) +make_drawpix_z_stencil_program_tgsi(struct st_context *st, + bool write_depth, + bool write_stencil) { struct ureg_program *ureg; struct ureg_src depth_sampler, stencil_sampler; struct ureg_src texcoord, color; struct ureg_dst out_color, out_depth, out_stencil; - const GLuint shaderIndex = write_depth * 2 + write_stencil; - void *cso; - - assert(shaderIndex < ARRAY_SIZE(st->drawpix.zs_shaders)); - - if (st->drawpix.zs_shaders[shaderIndex]) { - /* already have the proper shader */ - return st->drawpix.zs_shaders[shaderIndex]; - } ureg = ureg_create(PIPE_SHADER_FRAGMENT); if (ureg == NULL) @@ -180,7 +256,42 @@ get_drawpix_z_stencil_program(struct st_context *st, TGSI_TEXTURE_2D, texcoord, stencil_sampler); ureg_END(ureg); - cso = ureg_create_shader_and_destroy(ureg, st->pipe); + return ureg_create_shader_and_destroy(ureg, st->pipe); +} + + +/** + * Create fragment program that does a TEX() instruction to get a Z and/or + * stencil value value, then writes to FRAG_RESULT_DEPTH/FRAG_RESULT_STENCIL. + * Used for glDrawPixels(GL_DEPTH_COMPONENT / GL_STENCIL_INDEX). + * Pass fragment color through as-is. + * + * \return CSO of the fragment shader. + */ +static void * +get_drawpix_z_stencil_program(struct st_context *st, + bool write_depth, + bool write_stencil) +{ + struct pipe_screen *pscreen = st->pipe->screen; + const GLuint shaderIndex = write_depth * 2 + write_stencil; + void *cso; + + assert(shaderIndex < ARRAY_SIZE(st->drawpix.zs_shaders)); + + if (st->drawpix.zs_shaders[shaderIndex]) { + /* already have the proper shader */ + return st->drawpix.zs_shaders[shaderIndex]; + } + + enum pipe_shader_ir preferred_ir = + pscreen->get_shader_param(pscreen, PIPE_SHADER_FRAGMENT, + PIPE_SHADER_CAP_PREFERRED_IR); + + if (preferred_ir == PIPE_SHADER_IR_NIR) + cso = make_drawpix_z_stencil_program_nir(st, write_depth, write_stencil); + else + cso = make_drawpix_z_stencil_program_tgsi(st, write_depth, write_stencil); /* save the new shader */ st->drawpix.zs_shaders[shaderIndex] = cso; @@ -190,45 +301,44 @@ get_drawpix_z_stencil_program(struct st_context *st, /** * Create a simple vertex shader that just passes through the - * vertex position and texcoord (and optionally, color). + * vertex position, texcoord, and color. */ -static void * -make_passthrough_vertex_shader(struct st_context *st, - GLboolean passColor) +void +st_make_passthrough_vertex_shader(struct st_context *st) { - const unsigned texcoord_semantic = st->needs_texcoord_semantic ? - TGSI_SEMANTIC_TEXCOORD : TGSI_SEMANTIC_GENERIC; - - if (!st->drawpix.vert_shaders[passColor]) { - struct ureg_program *ureg = ureg_create( PIPE_SHADER_VERTEX ); + struct pipe_context *pipe = st->pipe; + struct pipe_screen *screen = pipe->screen; - if (ureg == NULL) - return NULL; + if (st->passthrough_vs) + return; - /* MOV result.pos, vertex.pos; */ - ureg_MOV(ureg, - ureg_DECL_output( ureg, TGSI_SEMANTIC_POSITION, 0 ), - ureg_DECL_vs_input( ureg, 0 )); + enum pipe_shader_ir preferred_ir = + screen->get_shader_param(screen, PIPE_SHADER_VERTEX, + PIPE_SHADER_CAP_PREFERRED_IR); - if (passColor) { - /* MOV result.color0, vertex.attr[1]; */ - ureg_MOV(ureg, - ureg_DECL_output( ureg, TGSI_SEMANTIC_COLOR, 0 ), - ureg_DECL_vs_input( ureg, 1 )); - } + if (preferred_ir == PIPE_SHADER_IR_NIR) { + unsigned inputs[] = + { VERT_ATTRIB_POS, VERT_ATTRIB_COLOR0, VERT_ATTRIB_GENERIC0 }; + unsigned outputs[] = + { VARYING_SLOT_POS, VARYING_SLOT_COL0, VARYING_SLOT_TEX0 }; - /* MOV result.texcoord0, vertex.attr[2]; */ - ureg_MOV(ureg, - ureg_DECL_output( ureg, texcoord_semantic, 0 ), - ureg_DECL_vs_input( ureg, 2 )); - - ureg_END( ureg ); - - st->drawpix.vert_shaders[passColor] = - ureg_create_shader_and_destroy( ureg, st->pipe ); + st->passthrough_vs = + st_nir_make_passthrough_shader(st, "drawpixels VS", + MESA_SHADER_VERTEX, 3, + inputs, outputs, NULL, 0); + } else { + const enum tgsi_semantic semantic_names[] = { + TGSI_SEMANTIC_POSITION, + TGSI_SEMANTIC_COLOR, + st->needs_texcoord_semantic ? TGSI_SEMANTIC_TEXCOORD : + TGSI_SEMANTIC_GENERIC + }; + const uint semantic_indexes[] = { 0, 0, 0 }; + + st->passthrough_vs = + util_make_vertex_passthrough_shader(st->pipe, 3, semantic_names, + semantic_indexes, false); } - - return st->drawpix.vert_shaders[passColor]; } @@ -374,6 +484,130 @@ alloc_texture(struct st_context *st, GLsizei width, GLsizei height, } +/** + * Search the cache for an image which matches the given parameters. + * \return pipe_resource pointer if found, NULL if not found. + */ +static struct pipe_resource * +search_drawpixels_cache(struct st_context *st, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const struct gl_pixelstore_attrib *unpack, + const void *pixels) +{ + struct pipe_resource *pt = NULL; + const GLint bpp = _mesa_bytes_per_pixel(format, type); + unsigned i; + + if ((unpack->RowLength != 0 && unpack->RowLength != width) || + unpack->SkipPixels != 0 || + unpack->SkipRows != 0 || + unpack->SwapBytes || + unpack->BufferObj) { + /* we don't allow non-default pixel unpacking values */ + return NULL; + } + + /* Search cache entries for a match */ + for (i = 0; i < ARRAY_SIZE(st->drawpix_cache.entries); i++) { + struct drawpix_cache_entry *entry = &st->drawpix_cache.entries[i]; + + if (width == entry->width && + height == entry->height && + format == entry->format && + type == entry->type && + pixels == entry->user_pointer && + entry->image) { + assert(entry->texture); + + /* check if the pixel data is the same */ + if (memcmp(pixels, entry->image, width * height * bpp) == 0) { + /* Success - found a cache match */ + pipe_resource_reference(&pt, entry->texture); + /* refcount of returned texture should be at least two here. One + * reference for the cache to hold on to, one for the caller (which + * it will release), and possibly more held by the driver. + */ + assert(pt->reference.count >= 2); + + /* update the age of this entry */ + entry->age = ++st->drawpix_cache.age; + + return pt; + } + } + } + + /* no cache match found */ + return NULL; +} + + +/** + * Find the oldest entry in the glDrawPixels cache. We'll replace this + * one when we need to store a new image. + */ +static struct drawpix_cache_entry * +find_oldest_drawpixels_cache_entry(struct st_context *st) +{ + unsigned oldest_age = ~0u, oldest_index = ~0u; + unsigned i; + + /* Find entry with oldest (lowest) age */ + for (i = 0; i < ARRAY_SIZE(st->drawpix_cache.entries); i++) { + const struct drawpix_cache_entry *entry = &st->drawpix_cache.entries[i]; + if (entry->age < oldest_age) { + oldest_age = entry->age; + oldest_index = i; + } + } + + assert(oldest_index != ~0u); + + return &st->drawpix_cache.entries[oldest_index]; +} + + +/** + * Try to save the given glDrawPixels image in the cache. + */ +static void +cache_drawpixels_image(struct st_context *st, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const struct gl_pixelstore_attrib *unpack, + const void *pixels, + struct pipe_resource *pt) +{ + if ((unpack->RowLength == 0 || unpack->RowLength == width) && + unpack->SkipPixels == 0 && + unpack->SkipRows == 0) { + const GLint bpp = _mesa_bytes_per_pixel(format, type); + struct drawpix_cache_entry *entry = + find_oldest_drawpixels_cache_entry(st); + assert(entry); + entry->width = width; + entry->height = height; + entry->format = format; + entry->type = type; + entry->user_pointer = pixels; + free(entry->image); + entry->image = malloc(width * height * bpp); + if (entry->image) { + memcpy(entry->image, pixels, width * height * bpp); + pipe_resource_reference(&entry->texture, pt); + entry->age = ++st->drawpix_cache.age; + } + else { + /* out of memory, free/disable cached texture */ + entry->width = 0; + entry->height = 0; + pipe_resource_reference(&entry->texture, NULL); + } + } +} + + /** * Make texture containing an image for glDrawPixels image. * If 'pixels' is NULL, leave the texture image data undefined. @@ -392,44 +626,11 @@ make_texture(struct st_context *st, GLenum baseInternalFormat; #if USE_DRAWPIXELS_CACHE - const GLint bpp = _mesa_bytes_per_pixel(format, type); - - /* Check if the glDrawPixels() parameters and state matches the cache */ - if (width == st->drawpix_cache.width && - height == st->drawpix_cache.height && - format == st->drawpix_cache.format && - type == st->drawpix_cache.type && - pixels == st->drawpix_cache.user_pointer && - !_mesa_is_bufferobj(unpack->BufferObj) && - (unpack->RowLength == 0 || unpack->RowLength == width) && - unpack->SkipPixels == 0 && - unpack->SkipRows == 0 && - unpack->SwapBytes == GL_FALSE && - st->drawpix_cache.image) { - assert(st->drawpix_cache.texture); - - /* check if the pixel data is the same */ - if (memcmp(pixels, st->drawpix_cache.image, width * height * bpp) == 0) { - /* OK, re-use the cached texture */ - pipe_resource_reference(&pt, st->drawpix_cache.texture); - /* refcount of returned texture should be at least two here. One - * reference for the cache to hold on to, one for the caller (which - * it will release), and possibly more held by the driver. - */ - assert(pt->reference.count >= 2); - return pt; - } - } - - /* discard the cached image and texture (if there is one) */ - st->drawpix_cache.width = 0; - st->drawpix_cache.height = 0; - st->drawpix_cache.user_pointer = NULL; - if (st->drawpix_cache.image) { - free(st->drawpix_cache.image); - st->drawpix_cache.image = NULL; + pt = search_drawpixels_cache(st, width, height, format, type, + unpack, pixels); + if (pt) { + return pt; } - pipe_resource_reference(&st->drawpix_cache.texture, NULL); #endif /* Choose a pixel format for the temp texture which will hold the @@ -443,8 +644,9 @@ make_texture(struct st_context *st, GLenum intFormat = internal_format(ctx, format, type); pipeFormat = st_choose_format(st, intFormat, format, type, - st->internal_target, 0, - PIPE_BIND_SAMPLER_VIEW, FALSE); + st->internal_target, 0, 0, + PIPE_BIND_SAMPLER_VIEW, + false, false); assert(pipeFormat != PIPE_FORMAT_NONE); } @@ -464,7 +666,6 @@ make_texture(struct st_context *st, { struct pipe_transfer *transfer; - GLboolean success; GLubyte *dest; const GLbitfield imageTransferStateSave = ctx->_ImageTransferState; @@ -475,7 +676,11 @@ make_texture(struct st_context *st, dest = pipe_transfer_map(pipe, pt, 0, 0, PIPE_TRANSFER_WRITE, 0, 0, width, height, &transfer); - + if (!dest) { + pipe_resource_reference(&pt, NULL); + _mesa_unmap_pbo_source(ctx, unpack); + return NULL; + } /* Put image into texture transfer. * Note that the image is actually going to be upside down in @@ -497,9 +702,9 @@ make_texture(struct st_context *st, format, type, /* src format/type */ pixels, /* data source */ unpack); - success = GL_TRUE; } else { + ASSERTED bool success; success = _mesa_texstore(ctx, 2, /* dims */ baseInternalFormat, /* baseInternalFormat */ mformat, /* mesa_format */ @@ -509,44 +714,23 @@ make_texture(struct st_context *st, format, type, /* src format/type */ pixels, /* data source */ unpack); + + assert(success); } /* unmap */ pipe_transfer_unmap(pipe, transfer); - assert(success); - /* restore */ ctx->_ImageTransferState = imageTransferStateSave; } - _mesa_unmap_pbo_source(ctx, unpack); - #if USE_DRAWPIXELS_CACHE - /* Save the glDrawPixels parameter and image in the cache */ - if ((unpack->RowLength == 0 || unpack->RowLength == width) && - unpack->SkipPixels == 0 && - unpack->SkipRows == 0) { - st->drawpix_cache.width = width; - st->drawpix_cache.height = height; - st->drawpix_cache.format = format; - st->drawpix_cache.type = type; - st->drawpix_cache.user_pointer = pixels; - assert(!st->drawpix_cache.image); - st->drawpix_cache.image = malloc(width * height * bpp); - if (st->drawpix_cache.image) { - memcpy(st->drawpix_cache.image, pixels, width * height * bpp); - pipe_resource_reference(&st->drawpix_cache.texture, pt); - } - else { - /* out of memory, free/disable cached texture */ - st->drawpix_cache.width = 0; - st->drawpix_cache.height = 0; - pipe_resource_reference(&st->drawpix_cache.texture, NULL); - } - } + cache_drawpixels_image(st, width, height, format, type, unpack, pixels, pt); #endif + _mesa_unmap_pbo_source(ctx, unpack); + return pt; } @@ -570,7 +754,7 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z, const unsigned fb_width = _mesa_geometric_width(ctx->DrawBuffer); const unsigned fb_height = _mesa_geometric_height(ctx->DrawBuffer); GLfloat x0, y0, x1, y1; - GLsizei maxSize; + ASSERTED GLsizei maxSize; boolean normalized = sv[0]->texture->target == PIPE_TEXTURE_2D; unsigned cso_state_mask; @@ -580,8 +764,8 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z, /* XXX if DrawPixels image is larger than max texture size, break * it up into chunks. */ - maxSize = 1 << (pipe->screen->get_param(pipe->screen, - PIPE_CAP_MAX_TEXTURE_2D_LEVELS) - 1); + maxSize = pipe->screen->get_param(pipe->screen, + PIPE_CAP_MAX_TEXTURE_2D_SIZE); assert(width <= maxSize); assert(height <= maxSize); @@ -607,7 +791,10 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z, ctx->Color._ClampFragmentColor; rasterizer.half_pixel_center = 1; rasterizer.bottom_edge_rule = 1; - rasterizer.depth_clip = !ctx->Transform.DepthClamp; + rasterizer.depth_clip_near = st->clamp_frag_depth_in_shader || + !ctx->Transform.DepthClampNear; + rasterizer.depth_clip_far = st->clamp_frag_depth_in_shader || + !ctx->Transform.DepthClampFar; rasterizer.scissor = ctx->Scissor.EnableFlags; cso_set_rasterizer(cso, &rasterizer); } @@ -667,11 +854,11 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z, const struct pipe_sampler_state *samplers[PIPE_MAX_SAMPLERS]; uint num = MAX3(fpv->drawpix_sampler + 1, fpv->pixelmap_sampler + 1, - st->state.num_samplers[PIPE_SHADER_FRAGMENT]); + st->state.num_frag_samplers); uint i; - for (i = 0; i < st->state.num_samplers[PIPE_SHADER_FRAGMENT]; i++) - samplers[i] = &st->state.samplers[PIPE_SHADER_FRAGMENT][i]; + for (i = 0; i < st->state.num_frag_samplers; i++) + samplers[i] = &st->state.frag_samplers[i]; samplers[fpv->drawpix_sampler] = &sampler; if (sv[1]) @@ -694,7 +881,7 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z, fpv->pixelmap_sampler + 1, st->state.num_sampler_views[PIPE_SHADER_FRAGMENT]); - memcpy(sampler_views, st->state.sampler_views[PIPE_SHADER_FRAGMENT], + memcpy(sampler_views, st->state.frag_sampler_views, sizeof(sampler_views)); sampler_views[fpv->drawpix_sampler] = sv[0]; @@ -709,7 +896,8 @@ draw_textured_quad(struct gl_context *ctx, GLint x, GLint y, GLfloat z, /* viewport state: viewport matching window dims */ cso_set_viewport_dims(cso, fb_width, fb_height, TRUE); - cso_set_vertex_elements(cso, 3, st->util_velems); + st->util_velems.count = 3; + cso_set_vertex_elements(cso, &st->util_velems); cso_set_stream_outputs(cso, 0, NULL, NULL); /* Compute Gallium window coords (y=0=top) with pixel zoom. @@ -789,7 +977,7 @@ draw_stencil_pixels(struct gl_context *ctx, GLint x, GLint y, y = ctx->DrawBuffer->Height - y - height; } - if (format == GL_STENCIL_INDEX && + if (format == GL_STENCIL_INDEX && _mesa_is_format_packed_depth_stencil(strb->Base.Format)) { /* writing stencil to a combined depth+stencil buffer */ usage = PIPE_TRANSFER_READ_WRITE; @@ -965,6 +1153,35 @@ get_color_fp_variant(struct st_context *st) return fpv; } +/** + * Get fragment program variant for a glDrawPixels command + * for COLOR_INDEX data + */ +static struct st_fp_variant * +get_color_index_fp_variant(struct st_context *st) +{ + struct gl_context *ctx = st->ctx; + struct st_fp_variant_key key; + struct st_fp_variant *fpv; + + memset(&key, 0, sizeof(key)); + + key.st = st->has_shareable_shaders ? NULL : st; + key.drawpixels = 1; + /* Since GL is always in RGBA mode MapColorFlag does not + * affect GL_COLOR_INDEX format. + * Scale and bias also never affect GL_COLOR_INDEX format. + */ + key.scaleAndBias = 0; + key.pixelMaps = 0; + key.clamp_color = st->clamp_frag_color_in_shader && + ctx->Color._ClampFragmentColor; + + fpv = st_get_fp_variant(st, st->fp, &key); + + return fpv; +} + /** * Clamp glDrawPixels width and height to the maximum texture size. @@ -973,9 +1190,8 @@ static void clamp_size(struct pipe_context *pipe, GLsizei *width, GLsizei *height, struct gl_pixelstore_attrib *unpack) { - const int maxSize = - 1 << (pipe->screen->get_param(pipe->screen, - PIPE_CAP_MAX_TEXTURE_2D_LEVELS) - 1); + const int maxSize = pipe->screen->get_param(pipe->screen, + PIPE_CAP_MAX_TEXTURE_2D_SIZE); if (*width > maxSize) { if (unpack->RowLength == 0) @@ -1051,6 +1267,34 @@ setup_sampler_swizzle(struct pipe_sampler_view *sv, GLenum format, GLenum type) } +/** + * Compute the effective raster z position. This performs depth-clamping + * if needed. + */ +static float +get_effective_raster_z(struct gl_context *ctx) +{ + float z = ctx->Current.RasterPos[2]; + if (st_context(ctx)->clamp_frag_depth_in_shader) { + GLfloat depth_near; + GLfloat depth_far; + if (ctx->ViewportArray[0].Near < ctx->ViewportArray[0].Far) { + depth_near = ctx->ViewportArray[0].Near; + depth_far = ctx->ViewportArray[0].Far; + } else { + depth_near = ctx->ViewportArray[0].Far; + depth_far = ctx->ViewportArray[0].Near; + } + + if (ctx->Transform.DepthClampNear) + z = MAX2(z, depth_near); + if (ctx->Transform.DepthClampFar) + z = MIN2(z, depth_far); + } + return z; +} + + /** * Called via ctx->Driver.DrawPixels() */ @@ -1060,7 +1304,7 @@ st_DrawPixels(struct gl_context *ctx, GLint x, GLint y, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const void *pixels) { - void *driver_vp, *driver_fp; + void *driver_fp; struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; GLboolean write_stencil = GL_FALSE, write_depth = GL_FALSE; @@ -1078,7 +1322,7 @@ st_DrawPixels(struct gl_context *ctx, GLint x, GLint y, st_flush_bitmap_cache(st); st_invalidate_readpix_cache(st); - st_validate_state(st, ST_PIPELINE_RENDER); + st_validate_state(st, ST_PIPELINE_META); /* Limit the size of the glDrawPixels to the max texture size. * Strictly speaking, that's not correct but since we don't handle @@ -1103,21 +1347,29 @@ st_DrawPixels(struct gl_context *ctx, GLint x, GLint y, return; } + /* Put glDrawPixels image into a texture */ + pt = make_texture(st, width, height, format, type, unpack, pixels); + if (!pt) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels"); + return; + } + + st_make_passthrough_vertex_shader(st); + /* * Get vertex/fragment shaders */ if (write_depth || write_stencil) { driver_fp = get_drawpix_z_stencil_program(st, write_depth, write_stencil); - driver_vp = make_passthrough_vertex_shader(st, GL_TRUE); } else { - fpv = get_color_fp_variant(st); + fpv = (format != GL_COLOR_INDEX) ? get_color_fp_variant(st) : + get_color_index_fp_variant(st); - driver_fp = fpv->driver_shader; - driver_vp = make_passthrough_vertex_shader(st, GL_FALSE); + driver_fp = fpv->base.driver_shader; - if (ctx->Pixel.MapColorFlag) { + if (ctx->Pixel.MapColorFlag && format != GL_COLOR_INDEX) { pipe_sampler_view_reference(&sv[1], st->pixel_xfer.pixelmap_sampler_view); num_sampler_view++; @@ -1129,13 +1381,6 @@ st_DrawPixels(struct gl_context *ctx, GLint x, GLint y, st_upload_constants(st, &st->fp->Base); } - /* Put glDrawPixels image into a texture */ - pt = make_texture(st, width, height, format, type, unpack, pixels); - if (!pt) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels"); - return; - } - /* create sampler view for the image */ sv[0] = st_create_texture_sampler_view(st->pipe, pt); if (!sv[0]) { @@ -1166,12 +1411,12 @@ st_DrawPixels(struct gl_context *ctx, GLint x, GLint y, num_sampler_view++; } - draw_textured_quad(ctx, x, y, ctx->Current.RasterPos[2], + draw_textured_quad(ctx, x, y, get_effective_raster_z(ctx), width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY, sv, num_sampler_view, - driver_vp, + st->passthrough_vs, driver_fp, fpv, ctx->Current.RasterColor, GL_FALSE, write_depth, write_stencil); @@ -1307,21 +1552,21 @@ blit_copy_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, struct gl_pixelstore_attrib pack, unpack; GLint readX, readY, readW, readH, drawX, drawY, drawW, drawH; - if (type == GL_COLOR && - ctx->Pixel.ZoomX == 1.0 && + if (ctx->Pixel.ZoomX == 1.0 && ctx->Pixel.ZoomY == 1.0 && - ctx->_ImageTransferState == 0x0 && - !ctx->Color.BlendEnabled && - !ctx->Color.AlphaEnabled && - (!ctx->Color.ColorLogicOpEnabled || ctx->Color.LogicOp == GL_COPY) && - !ctx->Depth.Test && - !ctx->Fog.Enabled && - !ctx->Stencil.Enabled && - !ctx->FragmentProgram.Enabled && - !ctx->VertexProgram.Enabled && - !ctx->_Shader->CurrentProgram[MESA_SHADER_FRAGMENT] && - !_mesa_ati_fragment_shader_enabled(ctx) && - ctx->DrawBuffer->_NumColorDrawBuffers == 1 && + (type != GL_COLOR || + (ctx->_ImageTransferState == 0x0 && + !ctx->Color.BlendEnabled && + !ctx->Color.AlphaEnabled && + (!ctx->Color.ColorLogicOpEnabled || ctx->Color.LogicOp == GL_COPY) && + !ctx->Depth.Test && + !ctx->Fog.Enabled && + !ctx->Stencil.Enabled && + !ctx->FragmentProgram.Enabled && + !ctx->VertexProgram.Enabled && + !ctx->_Shader->CurrentProgram[MESA_SHADER_FRAGMENT] && + !_mesa_ati_fragment_shader_enabled(ctx) && + ctx->DrawBuffer->_NumColorDrawBuffers == 1)) && !ctx->Query.CondRenderQuery && !ctx->Query.CurrentOcclusionObject) { struct st_renderbuffer *rbRead, *rbDraw; @@ -1354,8 +1599,18 @@ blit_copy_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, drawW = readW; drawH = readH; - rbRead = st_get_color_read_renderbuffer(ctx); - rbDraw = st_renderbuffer(ctx->DrawBuffer->_ColorDrawBuffers[0]); + if (type == GL_COLOR) { + rbRead = st_get_color_read_renderbuffer(ctx); + rbDraw = st_renderbuffer(ctx->DrawBuffer->_ColorDrawBuffers[0]); + } else if (type == GL_DEPTH || type == GL_DEPTH_STENCIL) { + rbRead = st_renderbuffer(ctx->ReadBuffer->Attachment[BUFFER_DEPTH].Renderbuffer); + rbDraw = st_renderbuffer(ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer); + } else if (type == GL_STENCIL) { + rbRead = st_renderbuffer(ctx->ReadBuffer->Attachment[BUFFER_STENCIL].Renderbuffer); + rbDraw = st_renderbuffer(ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer); + } else { + return false; + } /* Flip src/dst position depending on the orientation of buffers. */ if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { @@ -1396,19 +1651,29 @@ blit_copy_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, blit.dst.box.width = drawW; blit.dst.box.height = drawH; blit.dst.box.depth = 1; - blit.mask = PIPE_MASK_RGBA; blit.filter = PIPE_TEX_FILTER_NEAREST; + if (type == GL_COLOR) + blit.mask |= PIPE_MASK_RGBA; + if (type == GL_DEPTH) + blit.mask |= PIPE_MASK_Z; + if (type == GL_STENCIL) + blit.mask |= PIPE_MASK_S; + if (type == GL_DEPTH_STENCIL) + blit.mask |= PIPE_MASK_ZS; + if (ctx->DrawBuffer != ctx->WinSysDrawBuffer) st_window_rectangles_to_blit(ctx, &blit); if (screen->is_format_supported(screen, blit.src.format, blit.src.resource->target, blit.src.resource->nr_samples, + blit.src.resource->nr_storage_samples, PIPE_BIND_SAMPLER_VIEW) && screen->is_format_supported(screen, blit.dst.format, blit.dst.resource->target, blit.dst.resource->nr_samples, + blit.dst.resource->nr_storage_samples, PIPE_BIND_RENDER_TARGET)) { pipe->blit(pipe, &blit); return GL_TRUE; @@ -1429,7 +1694,7 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; struct st_renderbuffer *rbRead; - void *driver_vp, *driver_fp; + void *driver_fp; struct pipe_resource *pt; struct pipe_sampler_view *sv[2] = { NULL }; struct st_fp_variant *fpv = NULL; @@ -1439,30 +1704,34 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, GLboolean invertTex = GL_FALSE; GLint readX, readY, readW, readH; struct gl_pixelstore_attrib pack = ctx->DefaultPacking; + GLboolean write_stencil = GL_FALSE; + GLboolean write_depth = GL_FALSE; _mesa_update_draw_buffer_bounds(ctx, ctx->DrawBuffer); st_flush_bitmap_cache(st); st_invalidate_readpix_cache(st); - st_validate_state(st, ST_PIPELINE_RENDER); + st_validate_state(st, ST_PIPELINE_META); - if (type == GL_DEPTH_STENCIL) { - /* XXX make this more efficient */ + if (blit_copy_pixels(ctx, srcx, srcy, width, height, dstx, dsty, type)) + return; + + /* fallback if the driver can't do stencil exports */ + if (type == GL_DEPTH_STENCIL && + !pipe->screen->get_param(pipe->screen, PIPE_CAP_SHADER_STENCIL_EXPORT)) { st_CopyPixels(ctx, srcx, srcy, width, height, dstx, dsty, GL_STENCIL); st_CopyPixels(ctx, srcx, srcy, width, height, dstx, dsty, GL_DEPTH); return; } - if (type == GL_STENCIL) { - /* can't use texturing to do stencil */ + /* fallback if the driver can't do stencil exports */ + if (type == GL_STENCIL && + !pipe->screen->get_param(pipe->screen, PIPE_CAP_SHADER_STENCIL_EXPORT)) { copy_stencil_pixels(ctx, srcx, srcy, width, height, dstx, dsty); return; } - if (blit_copy_pixels(ctx, srcx, srcy, width, height, dstx, dsty, type)) - return; - /* * The subsequent code implements glCopyPixels by copying the source * pixels into a temporary texture that's then applied to a textured quad. @@ -1470,6 +1739,7 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, * are handled. */ + st_make_passthrough_vertex_shader(st); /* * Get vertex/fragment shaders @@ -1479,8 +1749,7 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, rbRead = st_get_color_read_renderbuffer(ctx); - driver_fp = fpv->driver_shader; - driver_vp = make_passthrough_vertex_shader(st, GL_FALSE); + driver_fp = fpv->base.driver_shader; if (ctx->Pixel.MapColorFlag) { pipe_sampler_view_reference(&sv[1], @@ -1492,56 +1761,67 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, * into the constant buffer, we need to update them */ st_upload_constants(st, &st->fp->Base); - } - else { - assert(type == GL_DEPTH); + } else if (type == GL_DEPTH) { rbRead = st_renderbuffer(ctx->ReadBuffer-> Attachment[BUFFER_DEPTH].Renderbuffer); - driver_fp = get_drawpix_z_stencil_program(st, GL_TRUE, GL_FALSE); - driver_vp = make_passthrough_vertex_shader(st, GL_TRUE); + } else if (type == GL_STENCIL) { + rbRead = st_renderbuffer(ctx->ReadBuffer-> + Attachment[BUFFER_STENCIL].Renderbuffer); + driver_fp = get_drawpix_z_stencil_program(st, GL_FALSE, GL_TRUE); + } else { + assert(type == GL_DEPTH_STENCIL); + rbRead = st_renderbuffer(ctx->ReadBuffer-> + Attachment[BUFFER_DEPTH].Renderbuffer); + driver_fp = get_drawpix_z_stencil_program(st, GL_TRUE, GL_TRUE); } + /* Choose the format for the temporary texture. */ srcFormat = rbRead->texture->format; srcBind = PIPE_BIND_SAMPLER_VIEW | (type == GL_COLOR ? PIPE_BIND_RENDER_TARGET : PIPE_BIND_DEPTH_STENCIL); if (!screen->is_format_supported(screen, srcFormat, st->internal_target, 0, - srcBind)) { + 0, srcBind)) { /* srcFormat is non-renderable. Find a compatible renderable format. */ if (type == GL_DEPTH) { srcFormat = st_choose_format(st, GL_DEPTH_COMPONENT, GL_NONE, - GL_NONE, st->internal_target, 0, - srcBind, FALSE); + GL_NONE, st->internal_target, 0, 0, + srcBind, false, false); + } + else if (type == GL_STENCIL) { + /* can't use texturing, fallback to copy */ + copy_stencil_pixels(ctx, srcx, srcy, width, height, dstx, dsty); + return; } else { assert(type == GL_COLOR); if (util_format_is_float(srcFormat)) { srcFormat = st_choose_format(st, GL_RGBA32F, GL_NONE, - GL_NONE, st->internal_target, 0, - srcBind, FALSE); + GL_NONE, st->internal_target, 0, 0, + srcBind, false, false); } else if (util_format_is_pure_sint(srcFormat)) { srcFormat = st_choose_format(st, GL_RGBA32I, GL_NONE, - GL_NONE, st->internal_target, 0, - srcBind, FALSE); + GL_NONE, st->internal_target, 0, 0, + srcBind, false, false); } else if (util_format_is_pure_uint(srcFormat)) { srcFormat = st_choose_format(st, GL_RGBA32UI, GL_NONE, - GL_NONE, st->internal_target, 0, - srcBind, FALSE); + GL_NONE, st->internal_target, 0, 0, + srcBind, false, false); } else if (util_format_is_snorm(srcFormat)) { srcFormat = st_choose_format(st, GL_RGBA16_SNORM, GL_NONE, - GL_NONE, st->internal_target, 0, - srcBind, FALSE); + GL_NONE, st->internal_target, 0, 0, + srcBind, false, false); } else { srcFormat = st_choose_format(st, GL_RGBA, GL_NONE, - GL_NONE, st->internal_target, 0, - srcBind, FALSE); + GL_NONE, st->internal_target, 0, 0, + srcBind, false, false); } } @@ -1590,6 +1870,25 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, return; } + /* Create a second sampler view to read stencil */ + if (type == GL_STENCIL || type == GL_DEPTH_STENCIL) { + write_stencil = GL_TRUE; + if (type == GL_DEPTH_STENCIL) + write_depth = GL_TRUE; + enum pipe_format stencil_format = + util_format_stencil_only(pt->format); + /* we should not be doing pixel map/transfer (see above) */ + assert(num_sampler_view == 1); + sv[1] = st_create_texture_sampler_view_format(st->pipe, pt, + stencil_format); + if (!sv[1]) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); + pipe_resource_reference(&pt, NULL); + pipe_sampler_view_reference(&sv[0], NULL); + return; + } + num_sampler_view++; + } /* Copy the src region to the temporary texture. */ { struct pipe_blit_info blit; @@ -1613,7 +1912,12 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, blit.dst.box.width = readW; blit.dst.box.height = readH; blit.dst.box.depth = 1; - blit.mask = util_format_get_mask(pt->format) & ~PIPE_MASK_S; + if (type == GL_DEPTH) + blit.mask = util_format_get_mask(pt->format) & ~PIPE_MASK_S; + else if (type == GL_STENCIL) + blit.mask = util_format_get_mask(pt->format) & ~PIPE_MASK_Z; + else + blit.mask = util_format_get_mask(pt->format); blit.filter = PIPE_TEX_FILTER_NEAREST; pipe->blit(pipe, &blit); @@ -1622,14 +1926,15 @@ st_CopyPixels(struct gl_context *ctx, GLint srcx, GLint srcy, /* OK, the texture 'pt' contains the src image/pixels. Now draw a * textured quad with that texture. */ - draw_textured_quad(ctx, dstx, dsty, ctx->Current.RasterPos[2], + + draw_textured_quad(ctx, dstx, dsty, get_effective_raster_z(ctx), width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY, sv, num_sampler_view, - driver_vp, + st->passthrough_vs, driver_fp, fpv, ctx->Current.Attrib[VERT_ATTRIB_COLOR0], - invertTex, GL_FALSE, GL_FALSE); + invertTex, write_depth, write_stencil); pipe_resource_reference(&pt, NULL); pipe_sampler_view_reference(&sv[0], NULL); @@ -1651,12 +1956,16 @@ st_destroy_drawpix(struct st_context *st) for (i = 0; i < ARRAY_SIZE(st->drawpix.zs_shaders); i++) { if (st->drawpix.zs_shaders[i]) - cso_delete_fragment_shader(st->cso_context, - st->drawpix.zs_shaders[i]); + st->pipe->delete_fs_state(st->pipe, st->drawpix.zs_shaders[i]); } - if (st->drawpix.vert_shaders[0]) - cso_delete_vertex_shader(st->cso_context, st->drawpix.vert_shaders[0]); - if (st->drawpix.vert_shaders[1]) - cso_delete_vertex_shader(st->cso_context, st->drawpix.vert_shaders[1]); + if (st->passthrough_vs) + st->pipe->delete_vs_state(st->pipe, st->passthrough_vs); + + /* Free cache data */ + for (i = 0; i < ARRAY_SIZE(st->drawpix_cache.entries); i++) { + struct drawpix_cache_entry *entry = &st->drawpix_cache.entries[i]; + free(entry->image); + pipe_resource_reference(&entry->texture, NULL); + } }