| /********************************************************** |
| * Copyright 2008-2009 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 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. |
| * |
| **********************************************************/ |
| |
| #include "util/u_inlines.h" |
| #include "pipe/p_defines.h" |
| #include "util/u_math.h" |
| #include "util/u_memory.h" |
| #include "util/u_bitmask.h" |
| |
| #include "svga_context.h" |
| #include "svga_hw_reg.h" |
| #include "svga_cmd.h" |
| |
| |
| static inline unsigned |
| svga_translate_blend_factor(const struct svga_context *svga, unsigned factor) |
| { |
| /* Note: there is no SVGA3D_BLENDOP_[INV]BLENDFACTORALPHA so |
| * we can't translate PIPE_BLENDFACTOR_[INV_]CONST_ALPHA properly. |
| */ |
| switch (factor) { |
| case PIPE_BLENDFACTOR_ZERO: return SVGA3D_BLENDOP_ZERO; |
| case PIPE_BLENDFACTOR_SRC_ALPHA: return SVGA3D_BLENDOP_SRCALPHA; |
| case PIPE_BLENDFACTOR_ONE: return SVGA3D_BLENDOP_ONE; |
| case PIPE_BLENDFACTOR_SRC_COLOR: return SVGA3D_BLENDOP_SRCCOLOR; |
| case PIPE_BLENDFACTOR_INV_SRC_COLOR: return SVGA3D_BLENDOP_INVSRCCOLOR; |
| case PIPE_BLENDFACTOR_DST_COLOR: return SVGA3D_BLENDOP_DESTCOLOR; |
| case PIPE_BLENDFACTOR_INV_DST_COLOR: return SVGA3D_BLENDOP_INVDESTCOLOR; |
| case PIPE_BLENDFACTOR_INV_SRC_ALPHA: return SVGA3D_BLENDOP_INVSRCALPHA; |
| case PIPE_BLENDFACTOR_DST_ALPHA: return SVGA3D_BLENDOP_DESTALPHA; |
| case PIPE_BLENDFACTOR_INV_DST_ALPHA: return SVGA3D_BLENDOP_INVDESTALPHA; |
| case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE: return SVGA3D_BLENDOP_SRCALPHASAT; |
| case PIPE_BLENDFACTOR_CONST_COLOR: return SVGA3D_BLENDOP_BLENDFACTOR; |
| case PIPE_BLENDFACTOR_INV_CONST_COLOR: return SVGA3D_BLENDOP_INVBLENDFACTOR; |
| case PIPE_BLENDFACTOR_CONST_ALPHA: |
| if (svga_have_vgpu10(svga)) |
| return SVGA3D_BLENDOP_BLENDFACTORALPHA; |
| else |
| return SVGA3D_BLENDOP_BLENDFACTOR; /* as close as we can get */ |
| case PIPE_BLENDFACTOR_INV_CONST_ALPHA: |
| if (svga_have_vgpu10(svga)) |
| return SVGA3D_BLENDOP_INVBLENDFACTORALPHA; |
| else |
| return SVGA3D_BLENDOP_INVBLENDFACTOR; /* as close as we can get */ |
| case PIPE_BLENDFACTOR_SRC1_COLOR: return SVGA3D_BLENDOP_SRC1COLOR; |
| case PIPE_BLENDFACTOR_INV_SRC1_COLOR: return SVGA3D_BLENDOP_INVSRC1COLOR; |
| case PIPE_BLENDFACTOR_SRC1_ALPHA: return SVGA3D_BLENDOP_SRC1ALPHA; |
| case PIPE_BLENDFACTOR_INV_SRC1_ALPHA: return SVGA3D_BLENDOP_INVSRC1ALPHA; |
| case 0: return SVGA3D_BLENDOP_ONE; |
| default: |
| assert(0); |
| return SVGA3D_BLENDOP_ZERO; |
| } |
| } |
| |
| static inline unsigned |
| svga_translate_blend_func(unsigned mode) |
| { |
| switch (mode) { |
| case PIPE_BLEND_ADD: return SVGA3D_BLENDEQ_ADD; |
| case PIPE_BLEND_SUBTRACT: return SVGA3D_BLENDEQ_SUBTRACT; |
| case PIPE_BLEND_REVERSE_SUBTRACT: return SVGA3D_BLENDEQ_REVSUBTRACT; |
| case PIPE_BLEND_MIN: return SVGA3D_BLENDEQ_MINIMUM; |
| case PIPE_BLEND_MAX: return SVGA3D_BLENDEQ_MAXIMUM; |
| default: |
| assert(0); |
| return SVGA3D_BLENDEQ_ADD; |
| } |
| } |
| |
| |
| /** |
| * Define a vgpu10 blend state object for the given |
| * svga blend state. |
| */ |
| static void |
| define_blend_state_object(struct svga_context *svga, |
| struct svga_blend_state *bs) |
| { |
| SVGA3dDXBlendStatePerRT perRT[SVGA3D_MAX_RENDER_TARGETS]; |
| unsigned try; |
| int i; |
| |
| assert(svga_have_vgpu10(svga)); |
| |
| bs->id = util_bitmask_add(svga->blend_object_id_bm); |
| |
| for (i = 0; i < SVGA3D_DX_MAX_RENDER_TARGETS; i++) { |
| perRT[i].blendEnable = bs->rt[i].blend_enable; |
| perRT[i].srcBlend = bs->rt[i].srcblend; |
| perRT[i].destBlend = bs->rt[i].dstblend; |
| perRT[i].blendOp = bs->rt[i].blendeq; |
| perRT[i].srcBlendAlpha = bs->rt[i].srcblend_alpha; |
| perRT[i].destBlendAlpha = bs->rt[i].dstblend_alpha; |
| perRT[i].blendOpAlpha = bs->rt[i].blendeq_alpha; |
| perRT[i].renderTargetWriteMask = bs->rt[i].writemask; |
| perRT[i].logicOpEnable = 0; |
| perRT[i].logicOp = SVGA3D_LOGICOP_COPY; |
| assert(perRT[i].srcBlend == perRT[0].srcBlend); |
| } |
| |
| /* Loop in case command buffer is full and we need to flush and retry */ |
| for (try = 0; try < 2; try++) { |
| enum pipe_error ret; |
| |
| ret = SVGA3D_vgpu10_DefineBlendState(svga->swc, |
| bs->id, |
| bs->alpha_to_coverage, |
| bs->independent_blend_enable, |
| perRT); |
| if (ret == PIPE_OK) |
| return; |
| svga_context_flush(svga, NULL); |
| } |
| } |
| |
| |
| static void * |
| svga_create_blend_state(struct pipe_context *pipe, |
| const struct pipe_blend_state *templ) |
| { |
| struct svga_context *svga = svga_context(pipe); |
| struct svga_blend_state *blend = CALLOC_STRUCT( svga_blend_state ); |
| unsigned i; |
| |
| if (!blend) |
| return NULL; |
| |
| /* Fill in the per-rendertarget blend state. We currently only |
| * support independent blend enable and colormask per render target. |
| */ |
| for (i = 0; i < PIPE_MAX_COLOR_BUFS; i++) { |
| /* No way to set this in SVGA3D, and no way to correctly implement it on |
| * top of D3D9 API. Instead we try to simulate with various blend modes. |
| */ |
| if (templ->logicop_enable) { |
| switch (templ->logicop_func) { |
| case PIPE_LOGICOP_XOR: |
| case PIPE_LOGICOP_INVERT: |
| blend->need_white_fragments = TRUE; |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_SUBTRACT; |
| break; |
| case PIPE_LOGICOP_CLEAR: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MINIMUM; |
| break; |
| case PIPE_LOGICOP_COPY: |
| blend->rt[i].blend_enable = FALSE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_ADD; |
| break; |
| case PIPE_LOGICOP_COPY_INVERTED: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_INVSRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_ADD; |
| break; |
| case PIPE_LOGICOP_NOOP: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_DESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_ADD; |
| break; |
| case PIPE_LOGICOP_SET: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MAXIMUM; |
| break; |
| case PIPE_LOGICOP_AND: |
| /* Approximate with minimum - works for the 0 & anything case: */ |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_SRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_DESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MINIMUM; |
| break; |
| case PIPE_LOGICOP_AND_REVERSE: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_SRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_INVDESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MINIMUM; |
| break; |
| case PIPE_LOGICOP_AND_INVERTED: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_INVSRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_DESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MINIMUM; |
| break; |
| case PIPE_LOGICOP_OR: |
| /* Approximate with maximum - works for the 1 | anything case: */ |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_SRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_DESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MAXIMUM; |
| break; |
| case PIPE_LOGICOP_OR_REVERSE: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_SRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_INVDESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MAXIMUM; |
| break; |
| case PIPE_LOGICOP_OR_INVERTED: |
| blend->rt[i].blend_enable = TRUE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_INVSRCCOLOR; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_DESTCOLOR; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_MAXIMUM; |
| break; |
| case PIPE_LOGICOP_NAND: |
| case PIPE_LOGICOP_NOR: |
| case PIPE_LOGICOP_EQUIV: |
| /* Fill these in with plausible values */ |
| blend->rt[i].blend_enable = FALSE; |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_ADD; |
| break; |
| default: |
| assert(0); |
| break; |
| } |
| blend->rt[i].srcblend_alpha = blend->rt[i].srcblend; |
| blend->rt[i].dstblend_alpha = blend->rt[i].dstblend; |
| blend->rt[i].blendeq_alpha = blend->rt[i].blendeq; |
| |
| if (templ->logicop_func == PIPE_LOGICOP_XOR) { |
| pipe_debug_message(&svga->debug.callback, CONFORMANCE, |
| "XOR logicop mode has limited support"); |
| } |
| else if (templ->logicop_func != PIPE_LOGICOP_COPY) { |
| pipe_debug_message(&svga->debug.callback, CONFORMANCE, |
| "general logicops are not supported"); |
| } |
| } |
| else { |
| /* Note: the vgpu10 device does not yet support independent |
| * blend terms per render target. Target[0] always specifies the |
| * blending terms. |
| */ |
| if (templ->independent_blend_enable || templ->rt[0].blend_enable) { |
| /* always use the 0th target's blending terms for now */ |
| blend->rt[i].srcblend = |
| svga_translate_blend_factor(svga, templ->rt[0].rgb_src_factor); |
| blend->rt[i].dstblend = |
| svga_translate_blend_factor(svga, templ->rt[0].rgb_dst_factor); |
| blend->rt[i].blendeq = |
| svga_translate_blend_func(templ->rt[0].rgb_func); |
| blend->rt[i].srcblend_alpha = |
| svga_translate_blend_factor(svga, templ->rt[0].alpha_src_factor); |
| blend->rt[i].dstblend_alpha = |
| svga_translate_blend_factor(svga, templ->rt[0].alpha_dst_factor); |
| blend->rt[i].blendeq_alpha = |
| svga_translate_blend_func(templ->rt[0].alpha_func); |
| |
| if (blend->rt[i].srcblend_alpha != blend->rt[i].srcblend || |
| blend->rt[i].dstblend_alpha != blend->rt[i].dstblend || |
| blend->rt[i].blendeq_alpha != blend->rt[i].blendeq) { |
| blend->rt[i].separate_alpha_blend_enable = TRUE; |
| } |
| } |
| else { |
| /* disabled - default blend terms */ |
| blend->rt[i].srcblend = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].dstblend = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].blendeq = SVGA3D_BLENDEQ_ADD; |
| blend->rt[i].srcblend_alpha = SVGA3D_BLENDOP_ONE; |
| blend->rt[i].dstblend_alpha = SVGA3D_BLENDOP_ZERO; |
| blend->rt[i].blendeq_alpha = SVGA3D_BLENDEQ_ADD; |
| } |
| |
| if (templ->independent_blend_enable) { |
| blend->rt[i].blend_enable = templ->rt[i].blend_enable; |
| } |
| else { |
| blend->rt[i].blend_enable = templ->rt[0].blend_enable; |
| } |
| } |
| |
| /* Some GL blend modes are not supported by the VGPU9 device (there's |
| * no equivalent of PIPE_BLENDFACTOR_[INV_]CONST_ALPHA). |
| * When we set this flag, we copy the constant blend alpha value |
| * to the R, G, B components. |
| * This works as long as the src/dst RGB blend factors doesn't use |
| * PIPE_BLENDFACTOR_CONST_COLOR and PIPE_BLENDFACTOR_CONST_ALPHA |
| * at the same time. There's no work-around for that. |
| */ |
| if (!svga_have_vgpu10(svga)) { |
| if (templ->rt[0].rgb_src_factor == PIPE_BLENDFACTOR_CONST_ALPHA || |
| templ->rt[0].rgb_dst_factor == PIPE_BLENDFACTOR_CONST_ALPHA || |
| templ->rt[0].rgb_src_factor == PIPE_BLENDFACTOR_INV_CONST_ALPHA || |
| templ->rt[0].rgb_dst_factor == PIPE_BLENDFACTOR_INV_CONST_ALPHA) { |
| blend->blend_color_alpha = TRUE; |
| } |
| } |
| |
| if (templ->independent_blend_enable) { |
| blend->rt[i].writemask = templ->rt[i].colormask; |
| } |
| else { |
| blend->rt[i].writemask = templ->rt[0].colormask; |
| } |
| } |
| |
| blend->independent_blend_enable = templ->independent_blend_enable; |
| |
| blend->alpha_to_coverage = templ->alpha_to_coverage; |
| |
| if (svga_have_vgpu10(svga)) { |
| define_blend_state_object(svga, blend); |
| } |
| |
| svga->hud.num_blend_objects++; |
| SVGA_STATS_COUNT_INC(svga_screen(svga->pipe.screen)->sws, |
| SVGA_STATS_COUNT_BLENDSTATE); |
| |
| return blend; |
| } |
| |
| |
| static void svga_bind_blend_state(struct pipe_context *pipe, |
| void *blend) |
| { |
| struct svga_context *svga = svga_context(pipe); |
| |
| svga->curr.blend = (struct svga_blend_state*)blend; |
| svga->dirty |= SVGA_NEW_BLEND; |
| } |
| |
| static void svga_delete_blend_state(struct pipe_context *pipe, |
| void *blend) |
| { |
| struct svga_context *svga = svga_context(pipe); |
| struct svga_blend_state *bs = |
| (struct svga_blend_state *) blend; |
| |
| if (bs->id != SVGA3D_INVALID_ID) { |
| enum pipe_error ret; |
| |
| ret = SVGA3D_vgpu10_DestroyBlendState(svga->swc, bs->id); |
| if (ret != PIPE_OK) { |
| svga_context_flush(svga, NULL); |
| ret = SVGA3D_vgpu10_DestroyBlendState(svga->swc, bs->id); |
| assert(ret == PIPE_OK); |
| } |
| |
| if (bs->id == svga->state.hw_draw.blend_id) |
| svga->state.hw_draw.blend_id = SVGA3D_INVALID_ID; |
| |
| util_bitmask_clear(svga->blend_object_id_bm, bs->id); |
| bs->id = SVGA3D_INVALID_ID; |
| } |
| |
| FREE(blend); |
| svga->hud.num_blend_objects--; |
| } |
| |
| static void svga_set_blend_color( struct pipe_context *pipe, |
| const struct pipe_blend_color *blend_color ) |
| { |
| struct svga_context *svga = svga_context(pipe); |
| |
| svga->curr.blend_color = *blend_color; |
| |
| svga->dirty |= SVGA_NEW_BLEND_COLOR; |
| } |
| |
| |
| void svga_init_blend_functions( struct svga_context *svga ) |
| { |
| svga->pipe.create_blend_state = svga_create_blend_state; |
| svga->pipe.bind_blend_state = svga_bind_blend_state; |
| svga->pipe.delete_blend_state = svga_delete_blend_state; |
| |
| svga->pipe.set_blend_color = svga_set_blend_color; |
| } |