/**************************************************************************
*
* Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
* 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, 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.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
*
**************************************************************************/
/**
* @file
*
* Wrap the cso cache & hash mechanisms in a simplified
* pipe-driver-specific interface.
*
* @author Zack Rusin <zack@tungstengraphics.com>
* @author Keith Whitwell <keith@tungstengraphics.com>
*/
#include "pipe/p_state.h"
#include "util/u_draw.h"
#include "util/u_framebuffer.h"
#include "util/u_inlines.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_vbuf.h"
#include "tgsi/tgsi_parse.h"
#include "cso_cache/cso_context.h"
#include "cso_cache/cso_cache.h"
#include "cso_cache/cso_hash.h"
#include "cso_context.h"
/**
* Info related to samplers and sampler views.
* We have one of these for fragment samplers and another for vertex samplers.
*/
struct sampler_info
{
struct {
void *samplers[PIPE_MAX_SAMPLERS];
unsigned nr_samplers;
} hw;
void *samplers[PIPE_MAX_SAMPLERS];
unsigned nr_samplers;
void *samplers_saved[PIPE_MAX_SAMPLERS];
unsigned nr_samplers_saved;
struct pipe_sampler_view *views[PIPE_MAX_SAMPLERS];
unsigned nr_views;
struct pipe_sampler_view *views_saved[PIPE_MAX_SAMPLERS];
unsigned nr_views_saved;
};
struct cso_context {
struct pipe_context *pipe;
struct cso_cache *cache;
struct u_vbuf *vbuf;
boolean has_geometry_shader;
boolean has_streamout;
struct sampler_info samplers[PIPE_SHADER_TYPES];
uint nr_vertex_buffers;
struct pipe_vertex_buffer vertex_buffers[PIPE_MAX_ATTRIBS];
uint nr_vertex_buffers_saved;
struct pipe_vertex_buffer vertex_buffers_saved[PIPE_MAX_ATTRIBS];
unsigned nr_so_targets;
struct pipe_stream_output_target *so_targets[PIPE_MAX_SO_BUFFERS];
unsigned nr_so_targets_saved;
struct pipe_stream_output_target *so_targets_saved[PIPE_MAX_SO_BUFFERS];
/** Current and saved state.
* The saved state is used as a 1-deep stack.
*/
void *blend, *blend_saved;
void *depth_stencil, *depth_stencil_saved;
void *rasterizer, *rasterizer_saved;
void *fragment_shader, *fragment_shader_saved;
void *vertex_shader, *vertex_shader_saved;
void *geometry_shader, *geometry_shader_saved;
void *velements, *velements_saved;
struct pipe_clip_state clip;
struct pipe_clip_state clip_saved;
struct pipe_framebuffer_state fb, fb_saved;
struct pipe_viewport_state vp, vp_saved;
struct pipe_blend_color blend_color;
unsigned sample_mask, sample_mask_saved;
struct pipe_stencil_ref stencil_ref, stencil_ref_saved;
};
static boolean delete_blend_state(struct cso_context *ctx, void *state)
{
struct cso_blend *cso = (struct cso_blend *)state;
if (ctx->blend == cso->data)
return FALSE;
if (cso->delete_state)
cso->delete_state(cso->context, cso->data);
FREE(state);
return TRUE;
}
static boolean delete_depth_stencil_state(struct cso_context *ctx, void *state)
{
struct cso_depth_stencil_alpha *cso =
(struct cso_depth_stencil_alpha *)state;
if (ctx->depth_stencil == cso->data)
return FALSE;
if (cso->delete_state)
cso->delete_state(cso->context, cso->data);
FREE(state);
return TRUE;
}
static boolean delete_sampler_state(struct cso_context *ctx, void *state)
{
struct cso_sampler *cso = (struct cso_sampler *)state;
if (cso->delete_state)
cso->delete_state(cso->context, cso->data);
FREE(state);
return TRUE;
}
static boolean delete_rasterizer_state(struct cso_context *ctx, void *state)
{
struct cso_rasterizer *cso = (struct cso_rasterizer *)state;
if (ctx->rasterizer == cso->data)
return FALSE;
if (cso->delete_state)
cso->delete_state(cso->context, cso->data);
FREE(state);
return TRUE;
}
static boolean delete_vertex_elements(struct cso_context *ctx,
void *state)
{
struct cso_velements *cso = (struct cso_velements *)state;
if (ctx->velements == cso->data)
return FALSE;
if (cso->delete_state)
cso->delete_state(cso->context, cso->data);
FREE(state);
return TRUE;
}
static INLINE boolean delete_cso(struct cso_context *ctx,
void *state, enum cso_cache_type type)
{
switch (type) {
case CSO_BLEND:
return delete_blend_state(ctx, state);
case CSO_SAMPLER:
return delete_sampler_state(ctx, state);
case CSO_DEPTH_STENCIL_ALPHA:
return delete_depth_stencil_state(ctx, state);
case CSO_RASTERIZER:
return delete_rasterizer_state(ctx, state);
case CSO_VELEMENTS:
return delete_vertex_elements(ctx, state);
default:
assert(0);
FREE(state);
}
return FALSE;
}
static INLINE void
sanitize_hash(struct cso_hash *hash, enum cso_cache_type type,
int max_size, void *user_data)
{
struct cso_context *ctx = (struct cso_context *)user_data;
/* if we're approach the maximum size, remove fourth of the entries
* otherwise every subsequent call will go through the same */
int hash_size = cso_hash_size(hash);
int max_entries = (max_size > hash_size) ? max_size : hash_size;
int to_remove = (max_size < max_entries) * max_entries/4;
struct cso_hash_iter iter = cso_hash_first_node(hash);
if (hash_size > max_size)
to_remove += hash_size - max_size;
while (to_remove) {
/*remove elements until we're good */
/*fixme: currently we pick the nodes to remove at random*/
void *cso = cso_hash_iter_data(iter);
if (delete_cso(ctx, cso, type)) {
iter = cso_hash_erase(hash, iter);
--to_remove;
} else
iter = cso_hash_iter_next(iter);
}
}
static void cso_init_vbuf(struct cso_context *cso)
{
struct u_vbuf_caps caps;
u_vbuf_get_caps(cso->pipe->screen, &caps);
/* Install u_vbuf if there is anything unsupported. */
if (!caps.buffer_offset_unaligned ||
!caps.buffer_stride_unaligned ||
!caps.velem_src_offset_unaligned ||
!caps.format_fixed32 ||
!caps.format_float16 ||
!caps.format_float64 ||
!caps.format_norm32 ||
!caps.format_scaled32 ||
!caps.user_vertex_buffers) {
cso->vbuf = u_vbuf_create(cso->pipe, &caps);
}
}
struct cso_context *cso_create_context( struct pipe_context *pipe )
{
struct cso_context *ctx = CALLOC_STRUCT(cso_context);
if (ctx == NULL)
goto out;
ctx->cache = cso_cache_create();
if (ctx->cache == NULL)
goto out;
cso_cache_set_sanitize_callback(ctx->cache,
sanitize_hash,
ctx);
ctx->pipe = pipe;
ctx->sample_mask_saved = ~0;
cso_init_vbuf(ctx);
/* Enable for testing: */
if (0) cso_set_maximum_cache_si