blob: 158cf995615c85d3d921cec2ba4f1530c97fe266 [file] [log] [blame] [edit]
/**************************************************************************
*
* Copyright © 2022 Intel Corporation
*
* 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 (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 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 "compiler/glsl/astc_glsl.h"
#include "compiler/glsl/bc1_glsl.h"
#include "compiler/glsl/bc4_glsl.h"
#include "compiler/glsl/cross_platform_settings_piece_all.h"
#include "compiler/glsl/etc2_rgba_stitch_glsl.h"
#include "main/context.h"
#include "main/shaderapi.h"
#include "main/shaderobj.h"
#include "main/texcompress_astc.h"
#include "util/texcompress_astc_luts_wrap.h"
#include "main/uniforms.h"
#include "state_tracker/st_atom_constbuf.h"
#include "state_tracker/st_bc1_tables.h"
#include "state_tracker/st_context.h"
#include "state_tracker/st_program.h"
#include "state_tracker/st_texcompress_compute.h"
#include "state_tracker/st_texture.h"
#include "util/u_hash_table.h"
#include "util/u_string.h"
enum compute_program_id {
COMPUTE_PROGRAM_BC1,
COMPUTE_PROGRAM_BC4,
COMPUTE_PROGRAM_STITCH,
COMPUTE_PROGRAM_ASTC_4x4,
COMPUTE_PROGRAM_ASTC_5x4,
COMPUTE_PROGRAM_ASTC_5x5,
COMPUTE_PROGRAM_ASTC_6x5,
COMPUTE_PROGRAM_ASTC_6x6,
COMPUTE_PROGRAM_ASTC_8x5,
COMPUTE_PROGRAM_ASTC_8x6,
COMPUTE_PROGRAM_ASTC_8x8,
COMPUTE_PROGRAM_ASTC_10x5,
COMPUTE_PROGRAM_ASTC_10x6,
COMPUTE_PROGRAM_ASTC_10x8,
COMPUTE_PROGRAM_ASTC_10x10,
COMPUTE_PROGRAM_ASTC_12x10,
COMPUTE_PROGRAM_ASTC_12x12,
COMPUTE_PROGRAM_COUNT
};
static struct gl_program * PRINTFLIKE(3, 4)
get_compute_program(struct st_context *st,
enum compute_program_id prog_id,
const char *source_fmt, ...)
{
/* Try to get the program from the cache. */
assert(prog_id < COMPUTE_PROGRAM_COUNT);
if (st->texcompress_compute.progs[prog_id])
return st->texcompress_compute.progs[prog_id];
/* Cache miss. Create the final source string. */
char *source_str;
va_list ap;
va_start(ap, source_fmt);
int num_printed_bytes = vasprintf(&source_str, source_fmt, ap);
va_end(ap);
if (num_printed_bytes == -1)
return NULL;
/* Compile and link the shader. Then, destroy the shader string. */
const char *strings[] = { source_str };
GLuint program =
_mesa_CreateShaderProgramv_impl(st->ctx, GL_COMPUTE_SHADER, 1, strings);
free(source_str);
struct gl_shader_program *shProg =
_mesa_lookup_shader_program(st->ctx, program);
if (!shProg)
return NULL;
if (shProg->data->LinkStatus == LINKING_FAILURE) {
fprintf(stderr, "Linking failed:\n%s\n", shProg->data->InfoLog);
_mesa_reference_shader_program(st->ctx, &shProg, NULL);
return NULL;
}
/* Cache the program and return it. */
return st->texcompress_compute.progs[prog_id] =
shProg->_LinkedShaders[MESA_SHADER_COMPUTE]->Program;
}
static struct pipe_resource *
create_bc1_endpoint_ssbo(struct pipe_context *pipe)
{
struct pipe_resource *buffer =
pipe_buffer_create(pipe->screen, PIPE_BIND_SHADER_BUFFER,
PIPE_USAGE_IMMUTABLE, sizeof(float) *
(sizeof(stb__OMatch5) + sizeof(stb__OMatch6)));
if (!buffer)
return NULL;
struct pipe_transfer *transfer;
float (*buffer_map)[2] = pipe_buffer_map(pipe, buffer,
PIPE_MAP_WRITE |
PIPE_MAP_DISCARD_WHOLE_RESOURCE,
&transfer);
if (!buffer_map) {
pipe_resource_reference(&buffer, NULL);
return NULL;
}
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 2; j++) {
buffer_map[i][j] = (float) stb__OMatch5[i][j];
buffer_map[i + 256][j] = (float) stb__OMatch6[i][j];
}
}
pipe_buffer_unmap(pipe, transfer);
return buffer;
}
static void
bind_compute_state(struct st_context *st,
struct gl_program *prog,
struct pipe_sampler_view **sampler_views,
const struct pipe_shader_buffer *shader_buffers,
const struct pipe_image_view *image_views,
bool cs_handle_from_prog,
bool constbuf0_from_prog)
{
assert(prog->info.stage == PIPE_SHADER_COMPUTE);
/* Set compute states in the same order as defined in st_atom_list.h */
assert(prog->affected_states & ST_NEW_CS_STATE);
assert(st->shader_has_one_variant[PIPE_SHADER_COMPUTE]);
cso_set_compute_shader_handle(st->cso_context,
cs_handle_from_prog ?
prog->variants->driver_shader : NULL);
if (prog->affected_states & ST_NEW_CS_SAMPLER_VIEWS) {
st->pipe->set_sampler_views(st->pipe, prog->info.stage, 0,
prog->info.num_textures, 0, false,
sampler_views);
}
if (prog->affected_states & ST_NEW_CS_SAMPLERS) {
/* Programs seem to set this bit more often than needed. For example, if
* a program only uses texelFetch, this shouldn't be needed. Section
* "11.1.3.2 Texel Fetches", of the GL 4.6 spec says:
*
* Texel fetch proceeds similarly to the steps described for texture
* access in section 11.1.3.5, with the exception that none of the
* operations controlled by sampler object state are performed,
*
* We assume that the program is using texelFetch or doesn't care about
* this state for a similar reason.
*
* See https://gitlab.freedesktop.org/mesa/mesa/-/issues/8014.
*/
}
if (prog->affected_states & ST_NEW_CS_CONSTANTS) {
st_upload_constants(st, constbuf0_from_prog ? prog : NULL,
prog->info.stage);
}
if (prog->affected_states & ST_NEW_CS_UBOS) {
unreachable("Uniform buffer objects not handled");
}
if (prog->affected_states & ST_NEW_CS_ATOMICS) {
unreachable("Atomic buffer objects not handled");
}
if (prog->affected_states & ST_NEW_CS_SSBOS) {
st->pipe->set_shader_buffers(st->pipe, prog->info.stage, 0,
prog->info.num_ssbos, shader_buffers,
prog->sh.ShaderStorageBlocksWriteAccess);
}
if (prog->affected_states & ST_NEW_CS_IMAGES) {
st->pipe->set_shader_images(st->pipe, prog->info.stage, 0,
prog->info.num_images, 0, image_views);
}
}
static void
dispatch_compute_state(struct st_context *st,
struct gl_program *prog,
struct pipe_sampler_view **sampler_views,
const struct pipe_shader_buffer *shader_buffers,
const struct pipe_image_view *image_views,
unsigned num_workgroups_x,
unsigned num_workgroups_y,
unsigned num_workgroups_z)
{
assert(prog->info.stage == PIPE_SHADER_COMPUTE);
/* Bind the state */
bind_compute_state(st, prog, sampler_views, shader_buffers, image_views,
true, true);
/* Launch the grid */
const struct pipe_grid_info info = {
.block[0] = prog->info.workgroup_size[0],
.block[1] = prog->info.workgroup_size[1],
.block[2] = prog->info.workgroup_size[2],
.grid[0] = num_workgroups_x,
.grid[1] = num_workgroups_y,
.grid[2] = num_workgroups_z,
};
st->pipe->launch_grid(st->pipe, &info);
/* Unbind the state */
bind_compute_state(st, prog, NULL, NULL, NULL, false, false);
/* If the previously used compute program was relying on any state that was
* trampled on by these state changes, dirty the relevant flags.
*/
if (st->cp) {
st->ctx->NewDriverState |=
st->cp->affected_states & prog->affected_states;
}
}
static struct pipe_resource *
cs_encode_bc1(struct st_context *st,
struct pipe_resource *rgba8_tex)
{
/* Create the required compute state */
struct gl_program *prog =
get_compute_program(st, COMPUTE_PROGRAM_BC1, bc1_source,
cross_platform_settings_piece_all_header);
if (!prog)
return NULL;
/* ... complete the program setup by defining the number of refinements to
* do on the created blocks. The program will attempt to create a more
* accurate encoding on each iteration. Doing at least one refinement
* provides a significant improvement in quality and is needed to give a
* result comparable to the CPU encoder (according to piglit tests).
* Additional refinements don't help as much.
*/
const unsigned num_refinements = 1;
_mesa_uniform(0, 1, &num_refinements, st->ctx, prog->shader_program,
GLSL_TYPE_UINT, 1);
const struct pipe_sampler_view templ = {
.target = PIPE_TEXTURE_2D,
.format = PIPE_FORMAT_R8G8B8A8_UNORM,
.swizzle_r = PIPE_SWIZZLE_X,
.swizzle_g = PIPE_SWIZZLE_Y,
.swizzle_b = PIPE_SWIZZLE_Z,
.swizzle_a = PIPE_SWIZZLE_W,
};
struct pipe_sampler_view *rgba8_view =
st->pipe->create_sampler_view(st->pipe, rgba8_tex, &templ);
if (!rgba8_view)
return NULL;
const struct pipe_shader_buffer ssbo = {
.buffer = st->texcompress_compute.bc1_endpoint_buf,
.buffer_size = st->texcompress_compute.bc1_endpoint_buf->width0,
};
struct pipe_resource *bc1_tex =
st_texture_create(st, PIPE_TEXTURE_2D, PIPE_FORMAT_R32G32_UINT, 0,
DIV_ROUND_UP(rgba8_tex->width0, 4),
DIV_ROUND_UP(rgba8_tex->height0, 4), 1, 1, 0,
PIPE_BIND_SHADER_IMAGE |
PIPE_BIND_SAMPLER_VIEW, false,
PIPE_COMPRESSION_FIXED_RATE_NONE);
if (!bc1_tex)
goto release_sampler_views;
const struct pipe_image_view image = {
.resource = bc1_tex,
.format = PIPE_FORMAT_R16G16B16A16_UINT,
.access = PIPE_IMAGE_ACCESS_WRITE,
.shader_access = PIPE_IMAGE_ACCESS_WRITE,
};
/* Dispatch the compute state */
dispatch_compute_state(st, prog, &rgba8_view, &ssbo, &image,
DIV_ROUND_UP(rgba8_tex->width0, 32),
DIV_ROUND_UP(rgba8_tex->height0, 32), 1);
release_sampler_views:
pipe_sampler_view_reference(&rgba8_view, NULL);
return bc1_tex;
}
static struct pipe_resource *
cs_encode_bc4(struct st_context *st,
struct pipe_resource *rgba8_tex,
enum pipe_swizzle component, bool use_snorm)
{
/* Create the required compute state */
struct gl_program *prog =
get_compute_program(st, COMPUTE_PROGRAM_BC4, bc4_source,
cross_platform_settings_piece_all_header);
if (!prog)
return NULL;
/* ... complete the program setup by picking the channel to encode and
* whether to encode it as snorm. The shader doesn't actually support
* channel index 2. So, pick index 0 and rely on swizzling instead.
*/
const unsigned params[] = { 0, use_snorm };
_mesa_uniform(0, 1, params, st->ctx, prog->shader_program,
GLSL_TYPE_UINT, 2);
const struct pipe_sampler_view templ = {
.target = PIPE_TEXTURE_2D,
.format = PIPE_FORMAT_R8G8B8A8_UNORM,
.swizzle_r = component,
.swizzle_g = PIPE_SWIZZLE_0,
.swizzle_b = PIPE_SWIZZLE_0,
.swizzle_a = PIPE_SWIZZLE_1,
};
struct pipe_sampler_view *rgba8_view =
st->pipe->create_sampler_view(st->pipe, rgba8_tex, &templ);
if (!rgba8_view)
return NULL;
struct pipe_resource *bc4_tex =
st_texture_create(st, PIPE_TEXTURE_2D, PIPE_FORMAT_R32G32_UINT, 0,
DIV_ROUND_UP(rgba8_tex->width0, 4),
DIV_ROUND_UP(rgba8_tex->height0, 4), 1, 1, 0,
PIPE_BIND_SHADER_IMAGE |
PIPE_BIND_SAMPLER_VIEW, false,
PIPE_COMPRESSION_FIXED_RATE_NONE);
if (!bc4_tex)
goto release_sampler_views;
const struct pipe_image_view image = {
.resource = bc4_tex,
.format = PIPE_FORMAT_R16G16B16A16_UINT,
.access = PIPE_IMAGE_ACCESS_WRITE,
.shader_access = PIPE_IMAGE_ACCESS_WRITE,
};
/* Dispatch the compute state */
dispatch_compute_state(st, prog, &rgba8_view, NULL, &image, 1,
DIV_ROUND_UP(rgba8_tex->width0, 16),
DIV_ROUND_UP(rgba8_tex->height0, 16));
release_sampler_views:
pipe_sampler_view_reference(&rgba8_view, NULL);
return bc4_tex;
}
static struct pipe_resource *
cs_stitch_64bpb_textures(struct st_context *st,
struct pipe_resource *tex_hi,
struct pipe_resource *tex_lo)
{
assert(util_format_get_blocksizebits(tex_hi->format) == 64);
assert(util_format_get_blocksizebits(tex_lo->format) == 64);
assert(tex_hi->width0 == tex_lo->width0);
assert(tex_hi->height0 == tex_lo->height0);
struct pipe_resource *stitched_tex = NULL;
/* Create the required compute state */
struct gl_program *prog =
get_compute_program(st, COMPUTE_PROGRAM_STITCH, etc2_rgba_stitch_source,
cross_platform_settings_piece_all_header);
if (!prog)
return NULL;
const struct pipe_sampler_view templ = {
.target = PIPE_TEXTURE_2D,
.format = PIPE_FORMAT_R32G32_UINT,
.swizzle_r = PIPE_SWIZZLE_X,
.swizzle_g = PIPE_SWIZZLE_Y,
.swizzle_b = PIPE_SWIZZLE_0,
.swizzle_a = PIPE_SWIZZLE_1,
};
struct pipe_sampler_view *rg32_views[2] = {
[0] = st->pipe->create_sampler_view(st->pipe, tex_hi, &templ),
[1] = st->pipe->create_sampler_view(st->pipe, tex_lo, &templ),
};
if (!rg32_views[0] || !rg32_views[1])
goto release_sampler_views;
stitched_tex =
st_texture_create(st, PIPE_TEXTURE_2D, PIPE_FORMAT_R32G32B32A32_UINT, 0,
tex_hi->width0,
tex_hi->height0, 1, 1, 0,
PIPE_BIND_SHADER_IMAGE |
PIPE_BIND_SAMPLER_VIEW, false,
PIPE_COMPRESSION_FIXED_RATE_NONE);
if (!stitched_tex)
goto release_sampler_views;
const struct pipe_image_view image = {
.resource = stitched_tex,
.format = PIPE_FORMAT_R32G32B32A32_UINT,
.access = PIPE_IMAGE_ACCESS_WRITE,
.shader_access = PIPE_IMAGE_ACCESS_WRITE,
};
/* Dispatch the compute state */
dispatch_compute_state(st, prog, rg32_views, NULL, &image,
DIV_ROUND_UP(tex_hi->width0, 8),
DIV_ROUND_UP(tex_hi->height0, 8), 1);
release_sampler_views:
pipe_sampler_view_reference(&rg32_views[0], NULL);
pipe_sampler_view_reference(&rg32_views[1], NULL);
return stitched_tex;
}
static struct pipe_resource *
cs_encode_bc3(struct st_context *st,
struct pipe_resource *rgba8_tex)
{
struct pipe_resource *bc3_tex = NULL;
/* Encode RGB channels as BC1. */
struct pipe_resource *bc1_tex = cs_encode_bc1(st, rgba8_tex);
if (!bc1_tex)
return NULL;
/* Encode alpha channels as BC4. */
struct pipe_resource *bc4_tex =
cs_encode_bc4(st, rgba8_tex, PIPE_SWIZZLE_W, false);
if (!bc4_tex)
goto release_textures;
st->pipe->memory_barrier(st->pipe, PIPE_BARRIER_TEXTURE);
/* Combine BC1 and BC4 to create BC3. */
bc3_tex = cs_stitch_64bpb_textures(st, bc1_tex, bc4_tex);
if (!bc3_tex)
goto release_textures;
release_textures:
pipe_resource_reference(&bc1_tex, NULL);
pipe_resource_reference(&bc4_tex, NULL);
return bc3_tex;
}
static struct pipe_resource *
sw_decode_astc(struct st_context *st,
uint8_t *astc_data,
unsigned astc_stride,
mesa_format astc_format,
unsigned width_px, unsigned height_px)
{
/* Create the destination */
struct pipe_resource *rgba8_tex =
st_texture_create(st, PIPE_TEXTURE_2D, PIPE_FORMAT_R8G8B8A8_UNORM, 0,
width_px, height_px, 1, 1, 0,
PIPE_BIND_SAMPLER_VIEW, false,
PIPE_COMPRESSION_FIXED_RATE_NONE);
if (!rgba8_tex)
return NULL;
/* Temporarily map the destination and decode into the returned pointer */
struct pipe_transfer *rgba8_xfer;
void *rgba8_map = pipe_texture_map(st->pipe, rgba8_tex, 0, 0,
PIPE_MAP_WRITE, 0, 0,
width_px, height_px, &rgba8_xfer);
if (!rgba8_map) {
pipe_resource_reference(&rgba8_tex, NULL);
return NULL;
}
_mesa_unpack_astc_2d_ldr(rgba8_map, rgba8_xfer->stride,
astc_data, astc_stride,
width_px, height_px, astc_format);
pipe_texture_unmap(st->pipe, rgba8_xfer);
return rgba8_tex;
}
static struct pipe_sampler_view *
create_astc_cs_payload_view(struct st_context *st,
uint8_t *data, unsigned stride,
uint32_t width_el, uint32_t height_el)
{
const struct pipe_resource src_templ = {
.target = PIPE_TEXTURE_2D,
.format = PIPE_FORMAT_R32G32B32A32_UINT,
.bind = PIPE_BIND_SAMPLER_VIEW,
.usage = PIPE_USAGE_STAGING,
.width0 = width_el,
.height0 = height_el,
.depth0 = 1,
.array_size = 1,
};
struct pipe_resource *payload_res =
st->screen->resource_create(st->screen, &src_templ);
if (!payload_res)
return NULL;
struct pipe_box box;
u_box_origin_2d(width_el, height_el, &box);
st->pipe->texture_subdata(st->pipe, payload_res, 0, 0,
&box,
data,
stride,
0 /* unused */);
const struct pipe_sampler_view view_templ = {
.target = PIPE_TEXTURE_2D,
.format = payload_res->format,
.swizzle_r = PIPE_SWIZZLE_X,
.swizzle_g = PIPE_SWIZZLE_Y,
.swizzle_b = PIPE_SWIZZLE_Z,
.swizzle_a = PIPE_SWIZZLE_W,
};
struct pipe_sampler_view *view =
st->pipe->create_sampler_view(st->pipe, payload_res, &view_templ);
pipe_resource_reference(&payload_res, NULL);
return view;
}
static struct pipe_sampler_view *
get_astc_partition_table_view(struct st_context *st,
unsigned block_w,
unsigned block_h)
{
unsigned lut_width;
unsigned lut_height;
struct pipe_box ptable_box;
void *ptable_data =
_mesa_get_astc_decoder_partition_table(block_w, block_h, &lut_width, &lut_height);
u_box_origin_2d(lut_width, lut_height, &ptable_box);
struct pipe_sampler_view *view =
util_hash_table_get(st->texcompress_compute.astc_partition_tables,
ptable_data);
if (view)
return view;
struct pipe_resource *res =
st_texture_create(st, PIPE_TEXTURE_2D, PIPE_FORMAT_R8_UINT, 0,
ptable_box.width, ptable_box.height,
1, 1, 0,
PIPE_BIND_SAMPLER_VIEW, false,
PIPE_COMPRESSION_FIXED_RATE_NONE);
if (!res)
return NULL;
st->pipe->texture_subdata(st->pipe, res, 0, 0,
&ptable_box,
ptable_data,
ptable_box.width,
0 /* unused */);
const struct pipe_sampler_view templ = {
.target = PIPE_TEXTURE_2D,
.format = res->format,
.swizzle_r = PIPE_SWIZZLE_X,
.swizzle_g = PIPE_SWIZZLE_Y,
.swizzle_b = PIPE_SWIZZLE_Z,
.swizzle_a = PIPE_SWIZZLE_W,
};
view = st->pipe->create_sampler_view(st->pipe, res, &templ);
pipe_resource_reference(&res, NULL);
if (view) {
_mesa_hash_table_insert(st->texcompress_compute.astc_partition_tables,
ptable_data, view);
ASSERTED const unsigned max_entries =
COMPUTE_PROGRAM_ASTC_12x12 - COMPUTE_PROGRAM_ASTC_4x4 + 1;
assert(_mesa_hash_table_num_entries(
st->texcompress_compute.astc_partition_tables) < max_entries);
}
return view;
}
static struct pipe_resource *
cs_decode_astc(struct st_context *st,
uint8_t *astc_data,
unsigned astc_stride,
mesa_format astc_format,
unsigned width_px, unsigned height_px)
{
const enum compute_program_id astc_id = COMPUTE_PROGRAM_ASTC_4x4 +
util_format_linear(astc_format) - PIPE_FORMAT_ASTC_4x4;
unsigned block_w, block_h;
_mesa_get_format_block_size(astc_format, &block_w, &block_h);
struct gl_program *prog =
get_compute_program(st, astc_id, astc_source, block_w, block_h);
if (!prog)
return NULL;
struct pipe_sampler_view *ptable_view =
get_astc_partition_table_view(st, block_w, block_h);
if (!ptable_view)
return NULL;
struct pipe_sampler_view *payload_view =
create_astc_cs_payload_view(st, astc_data, astc_stride,
DIV_ROUND_UP(width_px, block_w),
DIV_ROUND_UP(height_px, block_h));
if (!payload_view)
return NULL;
/* Create the destination */
struct pipe_resource *rgba8_tex =
st_texture_create(st, PIPE_TEXTURE_2D, PIPE_FORMAT_R8G8B8A8_UNORM, 0,
width_px, height_px, 1, 1, 0,
PIPE_BIND_SAMPLER_VIEW, false,
PIPE_COMPRESSION_FIXED_RATE_NONE);
if (!rgba8_tex)
goto release_payload_view;
const struct pipe_image_view image = {
.resource = rgba8_tex,
.format = PIPE_FORMAT_R8G8B8A8_UINT,
.access = PIPE_IMAGE_ACCESS_WRITE,
.shader_access = PIPE_IMAGE_ACCESS_WRITE,
};
struct pipe_sampler_view *sampler_views[] = {
st->texcompress_compute.astc_luts[0],
st->texcompress_compute.astc_luts[1],
st->texcompress_compute.astc_luts[2],
st->texcompress_compute.astc_luts[3],
st->texcompress_compute.astc_luts[4],
ptable_view,
payload_view,
};
dispatch_compute_state(st, prog, sampler_views, NULL, &image,
DIV_ROUND_UP(payload_view->texture->width0, 2),
DIV_ROUND_UP(payload_view->texture->height0, 2),
1);
release_payload_view:
pipe_sampler_view_reference(&payload_view, NULL);
return rgba8_tex;
}
static struct pipe_sampler_view *
get_sampler_view_for_lut(struct pipe_context *pipe,
const astc_decoder_lut *lut)
{
struct pipe_resource *res =
pipe_buffer_create_with_data(pipe,
PIPE_BIND_SAMPLER_VIEW,
PIPE_USAGE_DEFAULT,
lut->size_B,
lut->data);
if (!res)
return NULL;
const struct pipe_sampler_view templ = {
.format = lut->format,
.target = PIPE_BUFFER,
.swizzle_r = PIPE_SWIZZLE_X,
.swizzle_g = PIPE_SWIZZLE_Y,
.swizzle_b = PIPE_SWIZZLE_Z,
.swizzle_a = PIPE_SWIZZLE_W,
.u.buf.offset = 0,
.u.buf.size = lut->size_B,
};
struct pipe_sampler_view *view =
pipe->create_sampler_view(pipe, res, &templ);
pipe_resource_reference(&res, NULL);
return view;
}
/* Initializes required resources for Granite ASTC GPU decode.
*
* There are 5 texture buffer objects and one additional texture required.
* We initialize 5 tbo's here and a single texture later during runtime.
*/
static bool
initialize_astc_decoder(struct st_context *st)
{
astc_decoder_lut_holder astc_lut_holder;
_mesa_init_astc_decoder_luts(&astc_lut_holder);
const astc_decoder_lut *luts[] = {
&astc_lut_holder.color_endpoint,
&astc_lut_holder.color_endpoint_unquant,
&astc_lut_holder.weights,
&astc_lut_holder.weights_unquant,
&astc_lut_holder.trits_quints,
};
for (unsigned i = 0; i < ARRAY_SIZE(luts); i++) {
st->texcompress_compute.astc_luts[i] =
get_sampler_view_for_lut(st->pipe, luts[i]);
if (!st->texcompress_compute.astc_luts[i])
return false;
}
st->texcompress_compute.astc_partition_tables =
_mesa_pointer_hash_table_create(NULL);
if (!st->texcompress_compute.astc_partition_tables)
return false;
return true;
}
bool
st_init_texcompress_compute(struct st_context *st)
{
st->texcompress_compute.progs =
calloc(COMPUTE_PROGRAM_COUNT, sizeof(struct gl_program *));
if (!st->texcompress_compute.progs)
return false;
st->texcompress_compute.bc1_endpoint_buf =
create_bc1_endpoint_ssbo(st->pipe);
if (!st->texcompress_compute.bc1_endpoint_buf)
return false;
if (!initialize_astc_decoder(st))
return false;
return true;
}
static void
destroy_astc_decoder(struct st_context *st)
{
for (unsigned i = 0; i < ARRAY_SIZE(st->texcompress_compute.astc_luts); i++)
pipe_sampler_view_reference(&st->texcompress_compute.astc_luts[i], NULL);
if (st->texcompress_compute.astc_partition_tables) {
hash_table_foreach(st->texcompress_compute.astc_partition_tables,
entry) {
pipe_sampler_view_reference(
(struct pipe_sampler_view **)&entry->data, NULL);
}
}
_mesa_hash_table_destroy(st->texcompress_compute.astc_partition_tables,
NULL);
}
void
st_destroy_texcompress_compute(struct st_context *st)
{
/* The programs in the array are part of the gl_context (in st->ctx).They
* are automatically destroyed when the context is destroyed (via
* _mesa_free_context_data -> ... -> free_shader_program_data_cb).
*/
free(st->texcompress_compute.progs);
/* Destroy the SSBO used by the BC1 shader program. */
pipe_resource_reference(&st->texcompress_compute.bc1_endpoint_buf, NULL);
destroy_astc_decoder(st);
}
/* See st_texcompress_compute.h for more information. */
bool
st_compute_transcode_astc_to_dxt5(struct st_context *st,
uint8_t *astc_data,
unsigned astc_stride,
mesa_format astc_format,
struct pipe_resource *dxt5_tex,
unsigned dxt5_level,
unsigned dxt5_layer)
{
assert(_mesa_has_compute_shaders(st->ctx));
assert(_mesa_is_format_astc_2d(astc_format));
assert(dxt5_tex->format == PIPE_FORMAT_DXT5_RGBA ||
dxt5_tex->format == PIPE_FORMAT_DXT5_SRGBA);
assert(dxt5_level <= dxt5_tex->last_level);
assert(dxt5_layer <= util_max_layer(dxt5_tex, dxt5_level));
bool success = false;
/* Decode ASTC to RGBA8. */
struct pipe_resource *rgba8_tex =
cs_decode_astc(st, astc_data, astc_stride, astc_format,
u_minify(dxt5_tex->width0, dxt5_level),
u_minify(dxt5_tex->height0, dxt5_level));
if (!rgba8_tex)
return false;
st->pipe->memory_barrier(st->pipe, PIPE_BARRIER_TEXTURE);
/* Encode RGBA8 to BC3. */
struct pipe_resource *bc3_tex = cs_encode_bc3(st, rgba8_tex);
if (!bc3_tex)
goto release_textures;
/* Upload the result. */
struct pipe_box src_box;
u_box_origin_2d(bc3_tex->width0, bc3_tex->height0, &src_box);
st->pipe->resource_copy_region(st->pipe, dxt5_tex, dxt5_level,
0, 0, dxt5_layer, bc3_tex, 0, &src_box);
success = true;
release_textures:
pipe_resource_reference(&rgba8_tex, NULL);
pipe_resource_reference(&bc3_tex, NULL);
return success;
}