1 /********************************************************** 2 * Copyright 2008-2009 VMware, Inc. All rights reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 **********************************************************/ 25 26 #include "pipe/p_state.h" 27 #include "pipe/p_context.h" 28 #include "util/u_memory.h" 29 30 #include "svga_cmd.h" 31 #include "svga_context.h" 32 #include "svga_screen.h" 33 #include "svga_resource_buffer.h" 34 #include "svga_winsys.h" 35 #include "svga_debug.h" 36 37 38 /* Fixme: want a public base class for all pipe structs, even if there 39 * isn't much in them. 40 */ 41 struct pipe_query { 42 int dummy; 43 }; 44 45 struct svga_query { 46 struct pipe_query base; 47 SVGA3dQueryType type; 48 struct svga_winsys_buffer *hwbuf; 49 volatile SVGA3dQueryResult *queryResult; 50 struct pipe_fence_handle *fence; 51 }; 52 53 /*********************************************************************** 54 * Inline conversion functions. These are better-typed than the 55 * macros used previously: 56 */ 57 static INLINE struct svga_query * 58 svga_query( struct pipe_query *q ) 59 { 60 return (struct svga_query *)q; 61 } 62 63 static boolean svga_get_query_result(struct pipe_context *pipe, 64 struct pipe_query *q, 65 boolean wait, 66 union pipe_query_result *result); 67 68 static struct pipe_query *svga_create_query( struct pipe_context *pipe, 69 unsigned query_type ) 70 { 71 struct svga_context *svga = svga_context( pipe ); 72 struct svga_screen *svgascreen = svga_screen(pipe->screen); 73 struct svga_winsys_screen *sws = svgascreen->sws; 74 struct svga_query *sq; 75 76 SVGA_DBG(DEBUG_QUERY, "%s\n", __FUNCTION__); 77 78 sq = CALLOC_STRUCT(svga_query); 79 if (!sq) 80 goto no_sq; 81 82 sq->type = SVGA3D_QUERYTYPE_OCCLUSION; 83 84 sq->hwbuf = svga_winsys_buffer_create(svga, 85 1, 86 SVGA_BUFFER_USAGE_PINNED, 87 sizeof *sq->queryResult); 88 if(!sq->hwbuf) 89 goto no_hwbuf; 90 91 sq->queryResult = (SVGA3dQueryResult *)sws->buffer_map(sws, 92 sq->hwbuf, 93 PIPE_TRANSFER_WRITE); 94 if(!sq->queryResult) 95 goto no_query_result; 96 97 sq->queryResult->totalSize = sizeof *sq->queryResult; 98 sq->queryResult->state = SVGA3D_QUERYSTATE_NEW; 99 100 /* 101 * We request the buffer to be pinned and assume it is always mapped. 102 * 103 * The reason is that we don't want to wait for fences when checking the 104 * query status. 105 */ 106 sws->buffer_unmap(sws, sq->hwbuf); 107 108 return &sq->base; 109 110 no_query_result: 111 sws->buffer_destroy(sws, sq->hwbuf); 112 no_hwbuf: 113 FREE(sq); 114 no_sq: 115 return NULL; 116 } 117 118 static void svga_destroy_query(struct pipe_context *pipe, 119 struct pipe_query *q) 120 { 121 struct svga_screen *svgascreen = svga_screen(pipe->screen); 122 struct svga_winsys_screen *sws = svgascreen->sws; 123 struct svga_query *sq = svga_query( q ); 124 125 SVGA_DBG(DEBUG_QUERY, "%s\n", __FUNCTION__); 126 sws->buffer_destroy(sws, sq->hwbuf); 127 sws->fence_reference(sws, &sq->fence, NULL); 128 FREE(sq); 129 } 130 131 static void svga_begin_query(struct pipe_context *pipe, 132 struct pipe_query *q) 133 { 134 struct svga_screen *svgascreen = svga_screen(pipe->screen); 135 struct svga_winsys_screen *sws = svgascreen->sws; 136 struct svga_context *svga = svga_context( pipe ); 137 struct svga_query *sq = svga_query( q ); 138 enum pipe_error ret; 139 140 SVGA_DBG(DEBUG_QUERY, "%s\n", __FUNCTION__); 141 142 assert(!svga->sq); 143 144 /* Need to flush out buffered drawing commands so that they don't 145 * get counted in the query results. 146 */ 147 svga_hwtnl_flush_retry(svga); 148 149 if(sq->queryResult->state == SVGA3D_QUERYSTATE_PENDING) { 150 /* The application doesn't care for the pending query result. We cannot 151 * let go the existing buffer and just get a new one because its storage 152 * may be reused for other purposes and clobbered by the host when it 153 * determines the query result. So the only option here is to wait for 154 * the existing query's result -- not a big deal, given that no sane 155 * application would do this. 156 */ 157 uint64_t result; 158 159 svga_get_query_result(pipe, q, TRUE, (void*)&result); 160 161 assert(sq->queryResult->state != SVGA3D_QUERYSTATE_PENDING); 162 } 163 164 sq->queryResult->state = SVGA3D_QUERYSTATE_NEW; 165 sws->fence_reference(sws, &sq->fence, NULL); 166 167 ret = SVGA3D_BeginQuery(svga->swc, sq->type); 168 if(ret != PIPE_OK) { 169 svga_context_flush(svga, NULL); 170 ret = SVGA3D_BeginQuery(svga->swc, sq->type); 171 assert(ret == PIPE_OK); 172 } 173 174 svga->sq = sq; 175 } 176 177 static void svga_end_query(struct pipe_context *pipe, 178 struct pipe_query *q) 179 { 180 struct svga_context *svga = svga_context( pipe ); 181 struct svga_query *sq = svga_query( q ); 182 enum pipe_error ret; 183 184 SVGA_DBG(DEBUG_QUERY, "%s\n", __FUNCTION__); 185 assert(svga->sq == sq); 186 187 svga_hwtnl_flush_retry(svga); 188 189 /* Set to PENDING before sending EndQuery. */ 190 sq->queryResult->state = SVGA3D_QUERYSTATE_PENDING; 191 192 ret = SVGA3D_EndQuery( svga->swc, sq->type, sq->hwbuf); 193 if(ret != PIPE_OK) { 194 svga_context_flush(svga, NULL); 195 ret = SVGA3D_EndQuery( svga->swc, sq->type, sq->hwbuf); 196 assert(ret == PIPE_OK); 197 } 198 199 /* TODO: Delay flushing. We don't really need to flush here, just ensure 200 * that there is one flush before svga_get_query_result attempts to get the 201 * result */ 202 svga_context_flush(svga, NULL); 203 204 svga->sq = NULL; 205 } 206 207 static boolean svga_get_query_result(struct pipe_context *pipe, 208 struct pipe_query *q, 209 boolean wait, 210 union pipe_query_result *vresult) 211 { 212 struct svga_context *svga = svga_context( pipe ); 213 struct svga_screen *svgascreen = svga_screen( pipe->screen ); 214 struct svga_winsys_screen *sws = svgascreen->sws; 215 struct svga_query *sq = svga_query( q ); 216 SVGA3dQueryState state; 217 uint64_t *result = (uint64_t*)vresult; 218 219 SVGA_DBG(DEBUG_QUERY, "%s wait: %d\n", __FUNCTION__); 220 221 /* The query status won't be updated by the host unless 222 * SVGA_3D_CMD_WAIT_FOR_QUERY is emitted. Unfortunately this will cause a 223 * synchronous wait on the host */ 224 if(!sq->fence) { 225 enum pipe_error ret; 226 227 ret = SVGA3D_WaitForQuery( svga->swc, sq->type, sq->hwbuf); 228 if(ret != PIPE_OK) { 229 svga_context_flush(svga, NULL); 230 ret = SVGA3D_WaitForQuery( svga->swc, sq->type, sq->hwbuf); 231 assert(ret == PIPE_OK); 232 } 233 234 svga_context_flush(svga, &sq->fence); 235 236 assert(sq->fence); 237 } 238 239 state = sq->queryResult->state; 240 if(state == SVGA3D_QUERYSTATE_PENDING) { 241 if(!wait) 242 return FALSE; 243 244 sws->fence_finish(sws, sq->fence, SVGA_FENCE_FLAG_QUERY); 245 246 state = sq->queryResult->state; 247 } 248 249 assert(state == SVGA3D_QUERYSTATE_SUCCEEDED || 250 state == SVGA3D_QUERYSTATE_FAILED); 251 252 *result = (uint64_t)sq->queryResult->result32; 253 254 SVGA_DBG(DEBUG_QUERY, "%s result %d\n", __FUNCTION__, (unsigned)*result); 255 256 return TRUE; 257 } 258 259 260 261 void svga_init_query_functions( struct svga_context *svga ) 262 { 263 svga->pipe.create_query = svga_create_query; 264 svga->pipe.destroy_query = svga_destroy_query; 265 svga->pipe.begin_query = svga_begin_query; 266 svga->pipe.end_query = svga_end_query; 267 svga->pipe.get_query_result = svga_get_query_result; 268 } 269