| /********************************************************** |
| * 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 "svga_cmd.h" |
| |
| #include "pipe/p_defines.h" |
| #include "util/u_inlines.h" |
| #include "pipe/p_screen.h" |
| #include "util/u_memory.h" |
| #include "util/u_bitmask.h" |
| #include "util/u_upload_mgr.h" |
| |
| #include "svga_context.h" |
| #include "svga_screen.h" |
| #include "svga_surface.h" |
| #include "svga_resource_texture.h" |
| #include "svga_resource_buffer.h" |
| #include "svga_resource.h" |
| #include "svga_winsys.h" |
| #include "svga_swtnl.h" |
| #include "svga_draw.h" |
| #include "svga_debug.h" |
| #include "svga_state.h" |
| #include "svga_winsys.h" |
| |
| #define CONST0_UPLOAD_DEFAULT_SIZE 65536 |
| |
| DEBUG_GET_ONCE_BOOL_OPTION(no_swtnl, "SVGA_NO_SWTNL", FALSE) |
| DEBUG_GET_ONCE_BOOL_OPTION(force_swtnl, "SVGA_FORCE_SWTNL", FALSE); |
| DEBUG_GET_ONCE_BOOL_OPTION(use_min_mipmap, "SVGA_USE_MIN_MIPMAP", FALSE); |
| DEBUG_GET_ONCE_BOOL_OPTION(no_line_width, "SVGA_NO_LINE_WIDTH", FALSE); |
| DEBUG_GET_ONCE_BOOL_OPTION(force_hw_line_stipple, "SVGA_FORCE_HW_LINE_STIPPLE", FALSE); |
| |
| |
| static void |
| svga_destroy(struct pipe_context *pipe) |
| { |
| struct svga_context *svga = svga_context(pipe); |
| unsigned shader, i; |
| |
| /* free any alternate rasterizer states used for point sprite */ |
| for (i = 0; i < ARRAY_SIZE(svga->rasterizer_no_cull); i++) { |
| if (svga->rasterizer_no_cull[i]) { |
| pipe->delete_rasterizer_state(pipe, svga->rasterizer_no_cull[i]); |
| } |
| } |
| |
| /* free depthstencil_disable state */ |
| if (svga->depthstencil_disable) { |
| pipe->delete_depth_stencil_alpha_state(pipe, svga->depthstencil_disable); |
| } |
| |
| /* free HW constant buffers */ |
| for (shader = 0; shader < ARRAY_SIZE(svga->state.hw_draw.constbuf); shader++) { |
| pipe_resource_reference(&svga->state.hw_draw.constbuf[shader], NULL); |
| } |
| |
| pipe->delete_blend_state(pipe, svga->noop_blend); |
| |
| /* free query gb object */ |
| if (svga->gb_query) { |
| pipe->destroy_query(pipe, NULL); |
| svga->gb_query = NULL; |
| } |
| |
| util_blitter_destroy(svga->blitter); |
| |
| svga_cleanup_sampler_state(svga); |
| svga_cleanup_framebuffer(svga); |
| svga_cleanup_tss_binding(svga); |
| svga_cleanup_vertex_state(svga); |
| |
| svga_destroy_swtnl(svga); |
| svga_hwtnl_destroy(svga->hwtnl); |
| |
| svga->swc->destroy(svga->swc); |
| |
| util_bitmask_destroy(svga->blend_object_id_bm); |
| util_bitmask_destroy(svga->ds_object_id_bm); |
| util_bitmask_destroy(svga->input_element_object_id_bm); |
| util_bitmask_destroy(svga->rast_object_id_bm); |
| util_bitmask_destroy(svga->sampler_object_id_bm); |
| util_bitmask_destroy(svga->sampler_view_id_bm); |
| util_bitmask_destroy(svga->shader_id_bm); |
| util_bitmask_destroy(svga->surface_view_id_bm); |
| util_bitmask_destroy(svga->stream_output_id_bm); |
| util_bitmask_destroy(svga->query_id_bm); |
| u_upload_destroy(svga->const0_upload); |
| u_upload_destroy(svga->pipe.stream_uploader); |
| u_upload_destroy(svga->pipe.const_uploader); |
| svga_texture_transfer_map_upload_destroy(svga); |
| |
| /* free user's constant buffers */ |
| for (shader = 0; shader < PIPE_SHADER_TYPES; ++shader) { |
| for (i = 0; i < ARRAY_SIZE(svga->curr.constbufs[shader]); ++i) { |
| pipe_resource_reference(&svga->curr.constbufs[shader][i].buffer, NULL); |
| } |
| } |
| |
| FREE(svga); |
| } |
| |
| |
| struct pipe_context * |
| svga_context_create(struct pipe_screen *screen, void *priv, unsigned flags) |
| { |
| struct svga_screen *svgascreen = svga_screen(screen); |
| struct svga_context *svga = NULL; |
| enum pipe_error ret; |
| |
| SVGA_STATS_TIME_PUSH(svgascreen->sws, SVGA_STATS_TIME_CREATECONTEXT); |
| |
| svga = CALLOC_STRUCT(svga_context); |
| if (!svga) |
| goto cleanup; |
| |
| LIST_INITHEAD(&svga->dirty_buffers); |
| |
| svga->pipe.screen = screen; |
| svga->pipe.priv = priv; |
| svga->pipe.destroy = svga_destroy; |
| svga->pipe.stream_uploader = u_upload_create(&svga->pipe, 1024 * 1024, |
| PIPE_BIND_VERTEX_BUFFER | |
| PIPE_BIND_INDEX_BUFFER, |
| PIPE_USAGE_STREAM); |
| if (!svga->pipe.stream_uploader) |
| goto cleanup; |
| |
| svga->pipe.const_uploader = u_upload_create(&svga->pipe, 128 * 1024, |
| PIPE_BIND_CONSTANT_BUFFER, |
| PIPE_USAGE_STREAM); |
| if (!svga->pipe.const_uploader) |
| goto cleanup; |
| |
| svga->swc = svgascreen->sws->context_create(svgascreen->sws); |
| if (!svga->swc) |
| goto cleanup; |
| |
| svga_init_resource_functions(svga); |
| svga_init_blend_functions(svga); |
| svga_init_blit_functions(svga); |
| svga_init_depth_stencil_functions(svga); |
| svga_init_draw_functions(svga); |
| svga_init_flush_functions(svga); |
| svga_init_misc_functions(svga); |
| svga_init_rasterizer_functions(svga); |
| svga_init_sampler_functions(svga); |
| svga_init_fs_functions(svga); |
| svga_init_vs_functions(svga); |
| svga_init_gs_functions(svga); |
| svga_init_vertex_functions(svga); |
| svga_init_constbuffer_functions(svga); |
| svga_init_query_functions(svga); |
| svga_init_surface_functions(svga); |
| svga_init_stream_output_functions(svga); |
| svga_init_clear_functions(svga); |
| |
| /* init misc state */ |
| svga->curr.sample_mask = ~0; |
| |
| /* debug */ |
| svga->debug.no_swtnl = debug_get_option_no_swtnl(); |
| svga->debug.force_swtnl = debug_get_option_force_swtnl(); |
| svga->debug.use_min_mipmap = debug_get_option_use_min_mipmap(); |
| svga->debug.no_line_width = debug_get_option_no_line_width(); |
| svga->debug.force_hw_line_stipple = debug_get_option_force_hw_line_stipple(); |
| |
| if (!(svga->blend_object_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->ds_object_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->input_element_object_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->rast_object_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->sampler_object_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->sampler_view_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->shader_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->surface_view_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->stream_output_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| if (!(svga->query_id_bm = util_bitmask_create())) |
| goto cleanup; |
| |
| svga->hwtnl = svga_hwtnl_create(svga); |
| if (svga->hwtnl == NULL) |
| goto cleanup; |
| |
| if (!svga_init_swtnl(svga)) |
| goto cleanup; |
| |
| ret = svga_emit_initial_state(svga); |
| if (ret != PIPE_OK) |
| goto cleanup; |
| |
| svga->const0_upload = u_upload_create(&svga->pipe, |
| CONST0_UPLOAD_DEFAULT_SIZE, |
| PIPE_BIND_CONSTANT_BUFFER, |
| PIPE_USAGE_STREAM); |
| if (!svga->const0_upload) |
| goto cleanup; |
| |
| if (!svga_texture_transfer_map_upload_create(svga)) |
| goto cleanup; |
| |
| /* Avoid shortcircuiting state with initial value of zero. |
| */ |
| memset(&svga->state.hw_clear, 0xcd, sizeof(svga->state.hw_clear)); |
| memset(&svga->state.hw_clear.framebuffer, 0x0, |
| sizeof(svga->state.hw_clear.framebuffer)); |
| svga->state.hw_clear.num_rendertargets = 0; |
| svga->state.hw_clear.dsv = NULL; |
| |
| memset(&svga->state.hw_draw, 0xcd, sizeof(svga->state.hw_draw)); |
| memset(&svga->state.hw_draw.views, 0x0, sizeof(svga->state.hw_draw.views)); |
| memset(&svga->state.hw_draw.num_samplers, 0, |
| sizeof(svga->state.hw_draw.num_samplers)); |
| memset(&svga->state.hw_draw.num_sampler_views, 0, |
| sizeof(svga->state.hw_draw.num_sampler_views)); |
| memset(svga->state.hw_draw.sampler_views, 0, |
| sizeof(svga->state.hw_draw.sampler_views)); |
| svga->state.hw_draw.num_views = 0; |
| svga->state.hw_draw.num_backed_views = 0; |
| svga->state.hw_draw.rasterizer_discard = FALSE; |
| |
| /* Initialize the shader pointers */ |
| svga->state.hw_draw.vs = NULL; |
| svga->state.hw_draw.gs = NULL; |
| svga->state.hw_draw.fs = NULL; |
| |
| /* Initialize the currently bound buffer resources */ |
| memset(svga->state.hw_draw.constbuf, 0, |
| sizeof(svga->state.hw_draw.constbuf)); |
| memset(svga->state.hw_draw.default_constbuf_size, 0, |
| sizeof(svga->state.hw_draw.default_constbuf_size)); |
| memset(svga->state.hw_draw.enabled_constbufs, 0, |
| sizeof(svga->state.hw_draw.enabled_constbufs)); |
| svga->state.hw_draw.ib = NULL; |
| svga->state.hw_draw.num_vbuffers = 0; |
| memset(svga->state.hw_draw.vbuffers, 0, |
| sizeof(svga->state.hw_draw.vbuffers)); |
| svga->state.hw_draw.const0_buffer = NULL; |
| svga->state.hw_draw.const0_handle = NULL; |
| |
| /* Create a no-operation blend state which we will bind whenever the |
| * requested blend state is impossible (e.g. due to having an integer |
| * render target attached). |
| * |
| * XXX: We will probably actually need 16 of these, one for each possible |
| * RGBA color mask (4 bits). Then, we would bind the one with a color mask |
| * matching the blend state it is replacing. |
| */ |
| { |
| struct pipe_blend_state noop_tmpl = {0}; |
| unsigned i; |
| |
| for (i = 0; i < PIPE_MAX_COLOR_BUFS; ++i) { |
| // Set the color mask to all-ones. Later this may change. |
| noop_tmpl.rt[i].colormask = PIPE_MASK_RGBA; |
| } |
| svga->noop_blend = svga->pipe.create_blend_state(&svga->pipe, &noop_tmpl); |
| } |
| |
| svga->dirty = ~0; |
| svga->pred.query_id = SVGA3D_INVALID_ID; |
| svga->disable_rasterizer = FALSE; |
| |
| goto done; |
| |
| cleanup: |
| svga_destroy_swtnl(svga); |
| |
| if (svga->const0_upload) |
| u_upload_destroy(svga->const0_upload); |
| if (svga->pipe.const_uploader) |
| u_upload_destroy(svga->pipe.const_uploader); |
| if (svga->pipe.stream_uploader) |
| u_upload_destroy(svga->pipe.stream_uploader); |
| svga_texture_transfer_map_upload_destroy(svga); |
| if (svga->hwtnl) |
| svga_hwtnl_destroy(svga->hwtnl); |
| if (svga->swc) |
| svga->swc->destroy(svga->swc); |
| util_bitmask_destroy(svga->blend_object_id_bm); |
| util_bitmask_destroy(svga->ds_object_id_bm); |
| util_bitmask_destroy(svga->input_element_object_id_bm); |
| util_bitmask_destroy(svga->rast_object_id_bm); |
| util_bitmask_destroy(svga->sampler_object_id_bm); |
| util_bitmask_destroy(svga->sampler_view_id_bm); |
| util_bitmask_destroy(svga->shader_id_bm); |
| util_bitmask_destroy(svga->surface_view_id_bm); |
| util_bitmask_destroy(svga->stream_output_id_bm); |
| util_bitmask_destroy(svga->query_id_bm); |
| FREE(svga); |
| |
| done: |
| SVGA_STATS_TIME_POP(svgascreen->sws); |
| return svga ? &svga->pipe:NULL; |
| } |
| |
| |
| void |
| svga_context_flush(struct svga_context *svga, |
| struct pipe_fence_handle **pfence) |
| { |
| struct svga_screen *svgascreen = svga_screen(svga->pipe.screen); |
| struct pipe_fence_handle *fence = NULL; |
| uint64_t t0; |
| |
| SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_CONTEXTFLUSH); |
| |
| svga->curr.nr_fbs = 0; |
| |
| /* Unmap the 0th/default constant buffer. The u_upload_unmap() function |
| * will call pipe_context::transfer_flush_region() to indicate the |
| * region of the buffer which was modified (and needs to be uploaded). |
| */ |
| if (svga->state.hw_draw.const0_handle) { |
| assert(svga->state.hw_draw.const0_buffer); |
| u_upload_unmap(svga->const0_upload); |
| pipe_resource_reference(&svga->state.hw_draw.const0_buffer, NULL); |
| svga->state.hw_draw.const0_handle = NULL; |
| } |
| |
| /* Ensure that texture dma uploads are processed |
| * before submitting commands. |
| */ |
| svga_context_flush_buffers(svga); |
| |
| svga->hud.command_buffer_size += |
| svga->swc->get_command_buffer_size(svga->swc); |
| |
| /* Flush pending commands to hardware: |
| */ |
| t0 = svga_get_time(svga); |
| svga->swc->flush(svga->swc, &fence); |
| svga->hud.flush_time += (svga_get_time(svga) - t0); |
| |
| svga->hud.num_flushes++; |
| |
| svga_screen_cache_flush(svgascreen, svga, fence); |
| |
| SVGA3D_ResetLastCommand(svga->swc); |
| |
| /* To force the re-emission of rendertargets and texture sampler bindings on |
| * the next command buffer. |
| */ |
| svga->rebind.flags.rendertargets = TRUE; |
| svga->rebind.flags.texture_samplers = TRUE; |
| |
| if (svga_have_gb_objects(svga)) { |
| |
| svga->rebind.flags.constbufs = TRUE; |
| svga->rebind.flags.vs = TRUE; |
| svga->rebind.flags.fs = TRUE; |
| svga->rebind.flags.gs = TRUE; |
| |
| if (svga_need_to_rebind_resources(svga)) { |
| svga->rebind.flags.query = TRUE; |
| } |
| } |
| |
| if (SVGA_DEBUG & DEBUG_SYNC) { |
| if (fence) |
| svga->pipe.screen->fence_finish(svga->pipe.screen, NULL, fence, |
| PIPE_TIMEOUT_INFINITE); |
| } |
| |
| if (pfence) |
| svgascreen->sws->fence_reference(svgascreen->sws, pfence, fence); |
| |
| svgascreen->sws->fence_reference(svgascreen->sws, &fence, NULL); |
| |
| SVGA_STATS_TIME_POP(svga_sws(svga)); |
| } |
| |
| |
| /** |
| * Flush pending commands and wait for completion with a fence. |
| */ |
| void |
| svga_context_finish(struct svga_context *svga) |
| { |
| struct pipe_screen *screen = svga->pipe.screen; |
| struct pipe_fence_handle *fence = NULL; |
| |
| SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_CONTEXTFINISH); |
| |
| svga_context_flush(svga, &fence); |
| screen->fence_finish(screen, NULL, fence, PIPE_TIMEOUT_INFINITE); |
| screen->fence_reference(screen, &fence, NULL); |
| |
| SVGA_STATS_TIME_POP(svga_sws(svga)); |
| } |
| |
| |
| /** |
| * Emit pending drawing commands to the command buffer. |
| * If the command buffer overflows, we flush it and retry. |
| * \sa svga_hwtnl_flush() |
| */ |
| void |
| svga_hwtnl_flush_retry(struct svga_context *svga) |
| { |
| enum pipe_error ret = PIPE_OK; |
| |
| ret = svga_hwtnl_flush(svga->hwtnl); |
| if (ret == PIPE_ERROR_OUT_OF_MEMORY) { |
| svga_context_flush(svga, NULL); |
| ret = svga_hwtnl_flush(svga->hwtnl); |
| } |
| |
| assert(ret == PIPE_OK); |
| } |
| |
| |
| /** |
| * Flush the primitive queue if this buffer is referred. |
| * |
| * Otherwise DMA commands on the referred buffer will be emitted too late. |
| */ |
| void |
| svga_hwtnl_flush_buffer(struct svga_context *svga, |
| struct pipe_resource *buffer) |
| { |
| if (svga_hwtnl_is_buffer_referred(svga->hwtnl, buffer)) { |
| svga_hwtnl_flush_retry(svga); |
| } |
| } |
| |
| |
| /** |
| * Emit all operations pending on host surfaces. |
| */ |
| void |
| svga_surfaces_flush(struct svga_context *svga) |
| { |
| SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_SURFACEFLUSH); |
| |
| /* Emit buffered drawing commands. |
| */ |
| svga_hwtnl_flush_retry(svga); |
| |
| /* Emit back-copy from render target views to textures. |
| */ |
| svga_propagate_rendertargets(svga); |
| |
| SVGA_STATS_TIME_POP(svga_sws(svga)); |
| } |
| |
| |
| struct svga_winsys_context * |
| svga_winsys_context(struct pipe_context *pipe) |
| { |
| return svga_context(pipe)->swc; |
| } |