/**********************************************************
* 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 "util/u_memory.h"
#include "pipe/p_defines.h"
#include "util/u_math.h"
#include "svga_resource_texture.h"
#include "svga_sampler_view.h"
#include "svga_winsys.h"
#include "svga_context.h"
#include "svga_shader.h"
#include "svga_state.h"
#include "svga_cmd.h"
/**
* Called when tearing down a context to free resources and samplers.
*/
void
svga_cleanup_tss_binding(struct svga_context *svga)
{
const enum pipe_shader_type shader = PIPE_SHADER_FRAGMENT;
unsigned i;
for (i = 0; i < ARRAY_SIZE(svga->state.hw_draw.views); i++) {
struct svga_hw_view_state *view = &svga->state.hw_draw.views[i];
if (view) {
svga_sampler_view_reference(&view->v, NULL);
pipe_sampler_view_release(&svga->pipe,
&svga->curr.sampler_views[shader][i]);
pipe_resource_reference(&view->texture, NULL);
view->dirty = TRUE;
}
}
}
struct bind_queue {
struct {
unsigned unit;
struct svga_hw_view_state *view;
} bind[PIPE_MAX_SAMPLERS];
unsigned bind_count;
};
/**
* Update the texture binding for one texture unit.
*/
static void
emit_tex_binding_unit(struct svga_context *svga,
unsigned unit,
const struct svga_sampler_state *s,
const struct pipe_sampler_view *sv,
struct svga_hw_view_state *view,
boolean reemit,
struct bind_queue *queue)
{
struct pipe_resource *texture = NULL;
unsigned last_level, min_lod, max_lod;
/* get min max lod */
if (sv && s) {
if (s->mipfilter == SVGA3D_TEX_FILTER_NONE) {
/* just use the base level image */
min_lod = max_lod = sv->u.tex.first_level;
}
else {
last_level = MIN2(sv->u.tex.last_level, sv->texture->last_level);
min_lod = s->view_min_lod + sv->u.tex.first_level;
min_lod = MIN2(min_lod, last_level);
max_lod = MIN2(s->view_max_lod + sv->u.tex.first_level, last_level);
}
texture = sv->texture;
}
else {
min_lod = 0;
max_lod = 0;
}
if (view->texture != texture ||
view->min_lod != min_lod ||
view->max_lod != max_lod) {
svga_sampler_view_reference(&view->v, NULL);
pipe_resource_reference(&view->texture, texture);
view->dirty = TRUE;
view->min_lod = min_lod;
view->max_lod = max_lod;
if (texture) {
view->v = svga_get_tex_sampler_view(&svga->pipe,
texture,
min_lod,
max_lod);
}
}
/*
* We need to reemit non-null texture bindings, even when they are not
* dirty, to ensure that the resources are paged in.
*/
if (view->dirty || (reemit && view->v)) {
queue->bind[queue->bind_count].unit = unit;
queue->bind[queue->bind_count].view = view;
queue->bind_count++;
}
if (!view->dirty && view->v) {
svga_validate_sampler_view(svga, view->v);
}
}
static enum pipe_error
update_tss_binding(struct svga_context *svga, unsigned dirty)
{
const enum pipe_shader_type shader = PIPE_SHADER_FRAGMENT;
boolean reemit = svga->rebind.flags.texture_samplers;
unsigned i;
unsigned count = MAX2(svga->curr.num_sampler_views[shader],
svga->state.hw_draw.num_views);
struct bind_queue queue;
if (svga_have_vgpu10(svga))
return PIPE_OK;
queue.bind_count = 0;
for (i = 0; i < count; i++) {
emit_tex_binding_unit(svga, i,
svga->curr.sampler[shader][i],
svga->curr.sampler_views[shader][i],
&svga->state.hw_draw.views[i],
reemit,
&queue);
}
svga->state.hw_draw.num_views = svga->curr.num_sampler_views[shader];
/* Polygon stipple */
if (svga->curr.rast->templ.poly_stipple_enable) {
const unsigned unit = svga->state.hw_draw.fs->pstipple_sampler_unit;
emit_tex_binding_unit(svga, unit,
svga->polygon_stipple.sampler,
&svga->polygon_stipple.sampler_view->base,
&svga->state.hw_draw.views[unit],
reemit,
&queue);
}
svga->state.hw_draw.num_backed_views = 0;
if (queue.bind_count) {
SVGA3dTextureState *ts;
if (SVGA3D_BeginSetTextureState(svga->swc, &ts,
queue.bind_count) != PIPE_OK)
goto fail;
for (i = 0; i < queue.bind_count; i++) {
struct svga_winsys_surface *handle;
struct svga_hw_view_state *view = queue.bind[i].view;
ts[i].stage = queue.bind[i].unit;
ts[i].name = SVGA3D_TS_BIND_TEXTURE;
if (view->v) {
handle = view->v->handle;
/* Keep track of number of views with a backing copy
* of texture.
*/
if (handle != svga_texture(view->texture)->handle)
svga->state.hw_draw.num_backed_views++;
}
else {
handle = NULL;
}
svga->swc->surface_relocation(svga->swc,
&ts[i].value,
NULL,
handle,
SVGA_RELOC_READ);
queue.bind[i].view->dirty = FALSE;
}
SVGA_FIFOCommitAll(svga->swc);
}
svga->rebind.flags.texture_samplers = FALSE;
return PIPE_OK;
fail:
return PIPE_ERROR_OUT_OF_MEMORY;
}
/*
* Rebind textures.
*
* Similar to update_tss_binding, but without any state checking/update.
*
* Called at the beginning of every new command buffer to ensure that
* non-dirty textures are properly paged-in.
*/
enum pipe_error
svga_reemit_tss_bindings(struct svga_context *svga)
{
unsigned i;
enum pipe_error ret;
struct bind_queue queue;
assert(!svga_have_vgpu10(svga));
assert(svga->rebind.flags.texture_samplers);
queue.bind_count = 0;
for (i = 0; i < svga->state.hw_draw.num_views; i++) {
struct svga_hw_view_state *view = &svga->state.hw_draw.views[i];
if (view->v) {
queue.bind[queue.bind_count].unit = i;
queue.bind[queue.bind_count].view = view;
queue.bind_count++;
}
}
/* Polygon stipple */
if (svga->curr.rast->templ.poly_stipple_enable) {
const unsigned unit = svga->state.hw_draw.fs->pstipple_sampler_unit;
struct svga_hw_view_state *view = &svga->state.hw_draw.views[unit];
if (view->v) {
queue.bind[queue.bind_count].unit = unit;
queue.bind[queue.bind_count].view = view;
queue.bind_count++;
}
}
if (queue.bind_count) {
SVGA3dTextureState *ts;
ret = SVGA3D_BeginSetTextureState(svga->swc, &ts, queue.bind_count);
if (ret != PIPE_OK) {
return ret;
}
for (i = 0; i < queue.bind_count; i++) {
struct svga_winsys_surface *handle;
ts[i].stage = queue.bind[i].unit;
ts[i].name = SVGA3D_TS_BIND_TEXTURE;
assert(queue.bind[i].view->v);
handle = queue.bind[i].view->v->handle;
svga->swc->surface_relocation(svga->swc,
&ts[i].value,
NULL,
handle,
SVGA_RELOC_READ);
}
SVGA_FIFOCommitAll(svga->swc);
}
svga->rebind.flags.texture_samplers = FALSE;
return PIPE_OK;
}
struct svga_tracked_state svga_hw_tss_binding = {
"texture binding emit",
SVGA_NEW_FRAME_BUFFER |
SVGA_NEW_TEXTURE_BINDING |
SVGA_NEW_STIPPLE |
SVGA_NEW_SAMPLER,
update_tss_binding
};
struct ts_queue {
unsigned ts_count;
SVGA3dTextureState ts[PIPE_MAX_SAMPLERS*SVGA3D_TS_MAX];
};
static inline void
svga_queue_tss(struct ts_queue *q, unsigned unit, unsigned tss, unsigned value)
{
assert(q->ts_count < ARRAY_SIZE(q->ts));
q->ts[q->ts_count].stage = unit;
q->ts[q->ts_count].name = tss;
q->ts[q->ts_count].value = value;
q->ts_count++;
}
#define EMIT_TS(svga, unit, val, token) \
do { \
assert(unit < ARRAY_SIZE(svga->state.hw_draw.ts)); \
STATIC_ASSERT(SVGA3D_TS_##token < ARRAY_SIZE(svga->state.hw_draw.ts[unit])); \
if (svga->state.hw_draw.ts[unit][SVGA3D_TS_##token] != val) { \
svga_queue_tss(queue, unit, SVGA3D_TS_##token, val); \
svga->state.hw_draw.ts[unit][SVGA3D_TS_##token] = val; \
} \
} while (0)
#define EMIT_TS_FLOAT(svga, unit, fvalue, token) \
do { \
unsigned val = fui(fvalue); \
assert(unit < ARRAY_SIZE(svga->state.hw_draw.ts)); \
STATIC_ASSERT(SVGA3D_TS_##token < ARRAY_SIZE(svga->state.hw_draw.ts[unit])); \
if (svga->state.hw_draw.ts[unit][SVGA3D_TS_##token] != val) { \
svga_queue_tss(queue, unit, SVGA3D_TS_##token, val); \
svga->state.hw_draw.ts[unit][SVGA3D_TS_##token] = val; \
} \
} while (0)
/**
* Emit texture sampler state (tss) for one texture unit.
*/
static void
emit_tss_unit(struct svga_context *svga, unsigned unit,
const struct svga_sampler_state *state,
struct ts_queue *queue)
{
EMIT_TS(svga, unit, state->mipfilter, MIPFILTER);
EMIT_TS(svga, unit, state->min_lod, TEXTURE_MIPMAP_LEVEL);
EMIT_TS(svga, unit, state->magfilter, MAGFILTER);
EMIT_TS(svga, unit, state->minfilter, MINFILTER);
EMIT_TS(svga, unit, state->aniso_level, TEXTURE_ANISOTROPIC_LEVEL);
EMIT_TS_FLOAT(svga, unit, state->lod_bias, TEXTURE_LOD_BIAS);
EMIT_TS(svga, unit, state->addressu, ADDRESSU);
EMIT_TS(svga, unit, state->addressw, ADDRESSW);
EMIT_TS(svga, unit, state->bordercolor, BORDERCOLOR);
// TEXCOORDINDEX -- hopefully not needed
if (svga->curr.tex_flags.flag_1d & (1 << unit))
EMIT_TS(svga, unit, SVGA3D_TEX_ADDRESS_WRAP, ADDRESSV);
else
EMIT_TS(svga, unit, state->addressv, ADDRESSV);
if (svga->curr.tex_flags.flag_srgb & (1 << unit))
EMIT_TS_FLOAT(svga, unit, 2.2f, GAMMA);
else
EMIT_TS_FLOAT(svga, unit, 1.0f, GAMMA);
}
static enum pipe_error
update_tss(struct svga_context *svga, unsigned dirty)
{
const enum pipe_shader_type shader = PIPE_SHADER_FRAGMENT;
unsigned i;
struct ts_queue queue;
if (svga_have_vgpu10(svga))
return PIPE_OK;
queue.ts_count = 0;
for (i = 0; i < svga->curr.num_samplers[shader]; i++) {
if (svga->curr.sampler[shader][i]) {
const struct svga_sampler_state *curr = svga->curr.sampler[shader][i];
emit_tss_unit(svga, i, curr, &queue);
}
}
/* polygon stipple sampler */
if (svga->curr.rast->templ.poly_stipple_enable) {
emit_tss_unit(svga,
svga->state.hw_draw.fs->pstipple_sampler_unit,
svga->polygon_stipple.sampler,
&queue);
}
if (queue.ts_count) {
SVGA3dTextureState *ts;
if (SVGA3D_BeginSetTextureState(svga->swc, &ts, queue.ts_count) != PIPE_OK)
goto fail;
memcpy(ts, queue.ts, queue.ts_count * sizeof queue.ts[0]);
SVGA_FIFOCommitAll(svga->swc);
}
return PIPE_OK;
fail:
/* XXX: need to poison cached hardware state on failure to ensure
* dirty state gets re-emitted. Fix this by re-instating partial
* FIFOCommit command and only updating cached hw state once the
* initial allocation has succeeded.
*/
memset(svga->state.hw_draw.ts, 0xcd, sizeof(svga->state.hw_draw.ts));
return PIPE_ERROR_OUT_OF_MEMORY;
}
struct svga_tracked_state svga_hw_tss = {
"texture state emit",
(SVGA_NEW_SAMPLER |
SVGA_NEW_STIPPLE |
SVGA_NEW_TEXTURE_FLAGS),
update_tss
};