[Mesa-dev] [PATCH] gallium/util: Upload manager optimizations

Thomas Hellstrom thellstrom at vmware.com
Fri Mar 11 02:06:13 PST 2011


On 03/10/2011 04:57 PM, José Fonseca wrote:
> On Thu, 2011-03-10 at 06:01 -0800, Thomas Hellstrom wrote:
>    
>> Make sure that the upload manager doesn't upload data that's not
>> dirty. This speeds up the viewperf test proe-04/1 a factor 5 or so on svga.
>>      
> Sweet!
>
> A few comments inline
>
>    
>> Also introduce an u_upload_unmap() function that can be used
>> instead of u_upload_flush() so that we can pack
>> even more data in upload buffers. With this we can basically reuse the
>> upload buffer across flushes.
>>
>> Signed-off-by: Thomas Hellstrom<thellstrom at vmware.com>
>> ---
>>   src/gallium/auxiliary/util/u_upload_mgr.c |   41 ++++++++++++++++++++++------
>>   src/gallium/auxiliary/util/u_upload_mgr.h |   10 +++++++
>>   2 files changed, 42 insertions(+), 9 deletions(-)
>>
>> diff --git a/src/gallium/auxiliary/util/u_upload_mgr.c b/src/gallium/auxiliary/util/u_upload_mgr.c
>> index dcf800a..7768e13 100644
>> --- a/src/gallium/auxiliary/util/u_upload_mgr.c
>> +++ b/src/gallium/auxiliary/util/u_upload_mgr.c
>> @@ -51,6 +51,7 @@ struct u_upload_mgr {
>>      unsigned size;   /* Actual size of the upload buffer. */
>>      unsigned offset; /* Aligned offset to the upload buffer, pointing
>>                        * at the first unused byte. */
>> +   unsigned uploaded_offs; /* Offset below which data is already uploaded */
>>      
> It's not clear the difference between offset and uploaded_offs. More
> about this below.
>
>    
>>   };
>>
>>
>> @@ -72,6 +73,22 @@ struct u_upload_mgr *u_upload_create( struct pipe_context *pipe,
>>      return upload;
>>   }
>>
>> +void u_upload_unmap( struct u_upload_mgr *upload )
>> +{
>> +   if (upload->transfer) {
>> +      if (upload->size>  upload->uploaded_offs) {
>> +         pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer,
>> +                                        upload->uploaded_offs,
>> +                                        upload->offset - upload->uploaded_offs);
>> +      }
>> +      pipe_transfer_unmap(upload->pipe, upload->transfer);
>> +      pipe_transfer_destroy(upload->pipe, upload->transfer);
>> +      upload->transfer = NULL;
>> +      upload->uploaded_offs = upload->offset;
>> +      upload->map = NULL;
>> +   }
>> +}
>> +
>>   /* Release old buffer.
>>    *
>>    * This must usually be called prior to firing the command stream
>> @@ -84,17 +101,10 @@ struct u_upload_mgr *u_upload_create( struct pipe_context *pipe,
>>   void u_upload_flush( struct u_upload_mgr *upload )
>>   {
>>      /* Unmap and unreference the upload buffer. */
>> -   if (upload->transfer) {
>> -      if (upload->size) {
>> -         pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer,
>> -                                        0, upload->size);
>> -      }
>> -      pipe_transfer_unmap(upload->pipe, upload->transfer);
>> -      pipe_transfer_destroy(upload->pipe, upload->transfer);
>> -      upload->transfer = NULL;
>> -   }
>> +   u_upload_unmap(upload);
>>      pipe_resource_reference(&upload->buffer, NULL );
>>      upload->size = 0;
>> +   upload->uploaded_offs = 0;
>>   }
>>
>>
>> @@ -172,6 +182,19 @@ enum pipe_error u_upload_alloc( struct u_upload_mgr *upload,
>>
>>      offset = MAX2(upload->offset, alloc_offset);
>>
>> +   if (!upload->map) {
>> +      upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer,
>> +					  0, upload->size,
>> +					  PIPE_TRANSFER_WRITE |
>> +					  PIPE_TRANSFER_FLUSH_EXPLICIT |
>> +					  PIPE_TRANSFER_UNSYNCHRONIZED,
>> +					&upload->transfer);
>> +
>>      
> Instead of the whole range, we should mapping only the area of
> interest.
>
> That is, of [0, upload->size], it should be [upload->uploaded_offs,
> upload->size - uploaded_offs].
>
> This mean that the driver / kernel does not need to populate the bytes
> from [0, uploaded_offs] in the case where the contents is already gone
> to VRAM.
>
> I think uploaded_offs should be renamed to mapped_offset, i.e., "the
> offset from which the buffer is currently mapped".
>    

Agreed.


>    
>> +      assert(offset>= upload->uploaded_offs);
>> +      upload->uploaded_offs = offset;
>> +   }
>> +
>>      assert(offset<  upload->buffer->width0);
>>      assert(offset + size<= upload->buffer->width0);
>>      assert(size);
>> diff --git a/src/gallium/auxiliary/util/u_upload_mgr.h b/src/gallium/auxiliary/util/u_upload_mgr.h
>> index c9a2ffe..02426ea 100644
>> --- a/src/gallium/auxiliary/util/u_upload_mgr.h
>> +++ b/src/gallium/auxiliary/util/u_upload_mgr.h
>> @@ -67,6 +67,16 @@ void u_upload_destroy( struct u_upload_mgr *upload );
>>   void u_upload_flush( struct u_upload_mgr *upload );
>>
>>   /**
>> + * Unmap upload buffer
>> + *
>> + * \param upload           Upload manager
>> + *
>> + * This is like u_upload_flush() except the upload buffer is kept for
>> + * re-use across flushes. Allows us to pack more data into upload buffers.
>> + */
>> +void u_upload_unmap( struct u_upload_mgr *upload );
>>      
> These are two very different modes of operation.
>
> When doing this, the uploaded buffer should be created with
> PIPE_USAGE_STREAM and/or PIPE_USAGE_DYNAMIC (need to double check the
> smantics of this), so that the driver can put this buffer in an
> appropriate type of memory.
>
> When not doing this, then the uploaded buffer should use
> PIPE_USAGE_STATIC or IMMUTABLE (u_upload_mgr actually gets this wrong
> now).
>
> Instead of a new u_upload_unmap, I think that there should be a new flag
> a u_upload_mgr create, or this should be done always. Is there really a
> use case where we do not want to use this?
>    

Yes. IMHO the difference is resource management in the general case. 
Imagine a driver that is short on HW buffers for whatever reason. It 
detects that and calls a context flush to eventually release more 
buffers. That flush should release also the upload buffer, and the 
driver should call u_upload_flush() instead of u_upload_unmap(). My 
initial implementation in the SVGA driver used u_upload_unmap() for 
hwtnl flushes and u_upload_flush for svga context flushes.  This is more 
clearly illustrated if the u_upload_manager were to be extended with an 
optional small ring cache of hardware buffers.

/Thomas




> Jose
>
>    



More information about the mesa-dev mailing list