--- /dev/null
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2012-2015 LunarG, Inc.
+ *
+ * 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.
+ *
+ * Authors:
+ * Chia-I Wu <olv@lunarg.com>
+ */
+
+#include "util/u_half.h"
+
+#include "ilo_debug.h"
+#include "ilo_state_surface.h"
+#include "ilo_state_sampler.h"
+
+static bool
+sampler_validate_gen6_non_normalized(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ const enum gen_texcoord_mode addr_ctrls[3] = {
+ info->tcx_ctrl, info->tcy_ctrl, info->tcz_ctrl,
+ };
+ int i;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 98:
+ *
+ * "The following state must be set as indicated if this field
+ * (Non-normalized Coordinate Enable) is enabled:
+ *
+ * - TCX/Y/Z Address Control Mode must be TEXCOORDMODE_CLAMP,
+ * TEXCOORDMODE_HALF_BORDER, or TEXCOORDMODE_CLAMP_BORDER.
+ * - Surface Type must be SURFTYPE_2D or SURFTYPE_3D.
+ * - Mag Mode Filter must be MAPFILTER_NEAREST or
+ * MAPFILTER_LINEAR.
+ * - Min Mode Filter must be MAPFILTER_NEAREST or
+ * MAPFILTER_LINEAR.
+ * - Mip Mode Filter must be MIPFILTER_NONE.
+ * - Min LOD must be 0.
+ * - Max LOD must be 0.
+ * - MIP Count must be 0.
+ * - Surface Min LOD must be 0.
+ * - Texture LOD Bias must be 0."
+ */
+ for (i = 0; i < 3; i++) {
+ switch (addr_ctrls[i]) {
+ case GEN6_TEXCOORDMODE_CLAMP:
+ case GEN6_TEXCOORDMODE_CLAMP_BORDER:
+ case GEN8_TEXCOORDMODE_HALF_BORDER:
+ break;
+ default:
+ assert(!"bad non-normalized coordinate wrap mode");
+ break;
+ }
+ }
+
+ assert(info->mip_filter == GEN6_MIPFILTER_NONE);
+
+ assert((info->min_filter == GEN6_MAPFILTER_NEAREST ||
+ info->min_filter == GEN6_MAPFILTER_LINEAR) &&
+ (info->mag_filter == GEN6_MAPFILTER_NEAREST ||
+ info->mag_filter == GEN6_MAPFILTER_LINEAR));
+
+ assert(info->min_lod == 0.0f &&
+ info->max_lod == 0.0f &&
+ info->lod_bias == 0.0f);
+
+ return true;
+}
+
+static bool
+sampler_validate_gen6_sampler(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ if (info->non_normalized &&
+ !sampler_validate_gen6_non_normalized(dev, info))
+ return false;
+
+ if (ilo_dev_gen(dev) < ILO_GEN(8)) {
+ assert(info->tcx_ctrl != GEN8_TEXCOORDMODE_HALF_BORDER &&
+ info->tcy_ctrl != GEN8_TEXCOORDMODE_HALF_BORDER &&
+ info->tcz_ctrl != GEN8_TEXCOORDMODE_HALF_BORDER);
+ }
+
+ return true;
+}
+
+static uint32_t
+sampler_get_gen6_integer_filters(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ /*
+ * From the Sandy Bridge PRM, volume 4 part 1, page 103:
+ *
+ * "MIPFILTER_LINEAR is not supported for surface formats that do not
+ * support "Sampling Engine Filtering" as indicated in the Surface
+ * Formats table unless using the sample_c message type."
+ *
+ * "Only MAPFILTER_NEAREST is supported for surface formats that do not
+ * support "Sampling Engine Filtering" as indicated in the Surface
+ * Formats table unless using the sample_c message type.
+ */
+ const enum gen_mip_filter mip_filter =
+ (info->mip_filter == GEN6_MIPFILTER_LINEAR) ?
+ GEN6_MIPFILTER_NEAREST : info->mip_filter;
+ const enum gen_map_filter min_filter = GEN6_MAPFILTER_NEAREST;
+ const enum gen_map_filter mag_filter = GEN6_MAPFILTER_NEAREST;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ return mip_filter << GEN6_SAMPLER_DW0_MIP_FILTER__SHIFT |
+ mag_filter << GEN6_SAMPLER_DW0_MAG_FILTER__SHIFT |
+ min_filter << GEN6_SAMPLER_DW0_MIN_FILTER__SHIFT;
+}
+
+static uint32_t
+sampler_get_gen6_3d_filters(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ const enum gen_mip_filter mip_filter = info->mip_filter;
+ /*
+ * From the Sandy Bridge PRM, volume 4 part 1, page 103:
+ *
+ * "Only MAPFILTER_NEAREST and MAPFILTER_LINEAR are supported for
+ * surfaces of type SURFTYPE_3D."
+ */
+ const enum gen_map_filter min_filter =
+ (info->min_filter == GEN6_MAPFILTER_NEAREST ||
+ info->min_filter == GEN6_MAPFILTER_LINEAR) ?
+ info->min_filter : GEN6_MAPFILTER_LINEAR;
+ const enum gen_map_filter mag_filter =
+ (info->mag_filter == GEN6_MAPFILTER_NEAREST ||
+ info->mag_filter == GEN6_MAPFILTER_LINEAR) ?
+ info->mag_filter : GEN6_MAPFILTER_LINEAR;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ return mip_filter << GEN6_SAMPLER_DW0_MIP_FILTER__SHIFT |
+ mag_filter << GEN6_SAMPLER_DW0_MAG_FILTER__SHIFT |
+ min_filter << GEN6_SAMPLER_DW0_MIN_FILTER__SHIFT;
+}
+
+static uint32_t
+get_gen6_addr_controls(const struct ilo_dev *dev,
+ enum gen_texcoord_mode tcx_ctrl,
+ enum gen_texcoord_mode tcy_ctrl,
+ enum gen_texcoord_mode tcz_ctrl)
+{
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ if (ilo_dev_gen(dev) >= ILO_GEN(7)) {
+ return tcx_ctrl << GEN7_SAMPLER_DW3_U_WRAP__SHIFT |
+ tcy_ctrl << GEN7_SAMPLER_DW3_V_WRAP__SHIFT |
+ tcz_ctrl << GEN7_SAMPLER_DW3_R_WRAP__SHIFT;
+ } else {
+ return tcx_ctrl << GEN6_SAMPLER_DW1_U_WRAP__SHIFT |
+ tcy_ctrl << GEN6_SAMPLER_DW1_V_WRAP__SHIFT |
+ tcz_ctrl << GEN6_SAMPLER_DW1_R_WRAP__SHIFT;
+ }
+}
+
+static uint32_t
+sampler_get_gen6_1d_addr_controls(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ const enum gen_texcoord_mode tcx_ctrl =
+ (info->tcx_ctrl == GEN6_TEXCOORDMODE_CUBE) ?
+ GEN6_TEXCOORDMODE_CLAMP : info->tcx_ctrl;
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 100:
+ *
+ * "If this field (TCY Address Control Mode) is set to
+ * TEXCOORDMODE_CLAMP_BORDER or TEXCOORDMODE_HALF_BORDER and a 1D
+ * surface is sampled, incorrect blending with the border color in the
+ * vertical direction may occur."
+ */
+ const enum gen_texcoord_mode tcy_ctrl = GEN6_TEXCOORDMODE_CLAMP;
+ const enum gen_texcoord_mode tcz_ctrl = GEN6_TEXCOORDMODE_CLAMP;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ return get_gen6_addr_controls(dev, tcx_ctrl, tcy_ctrl, tcz_ctrl);
+}
+
+static uint32_t
+sampler_get_gen6_2d_3d_addr_controls(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ const enum gen_texcoord_mode tcx_ctrl =
+ (info->tcx_ctrl == GEN6_TEXCOORDMODE_CUBE) ?
+ GEN6_TEXCOORDMODE_CLAMP : info->tcx_ctrl;
+ const enum gen_texcoord_mode tcy_ctrl =
+ (info->tcy_ctrl == GEN6_TEXCOORDMODE_CUBE) ?
+ GEN6_TEXCOORDMODE_CLAMP : info->tcy_ctrl;
+ /*
+ * From the Sandy Bridge PRM, volume 4 part 1, page 108:
+ *
+ * "[DevSNB]: if this field (TCZ Address Control Mode) is set to
+ * TEXCOORDMODE_CLAMP_BORDER samples outside the map will clamp to 0
+ * instead of boarder color"
+ *
+ * From the Ivy Bridge PRM, volume 4 part 1, page 100:
+ *
+ * "If this field is set to TEXCOORDMODE_CLAMP_BORDER for 3D maps on
+ * formats without an alpha channel, samples straddling the map in the
+ * Z direction may have their alpha channels off by 1."
+ *
+ * Do we want to do something here?
+ */
+ const enum gen_texcoord_mode tcz_ctrl =
+ (info->tcz_ctrl == GEN6_TEXCOORDMODE_CUBE) ?
+ GEN6_TEXCOORDMODE_CLAMP : info->tcz_ctrl;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ return get_gen6_addr_controls(dev, tcx_ctrl, tcy_ctrl, tcz_ctrl);
+}
+
+static uint32_t
+sampler_get_gen6_cube_addr_controls(const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 99:
+ *
+ * "When using cube map texture coordinates, only TEXCOORDMODE_CLAMP
+ * and TEXCOORDMODE_CUBE settings are valid, and each TC component
+ * must have the same Address Control mode.
+ *
+ * When TEXCOORDMODE_CUBE is not used accessing a cube map, the map's
+ * Cube Face Enable field must be programmed to 111111b (all faces
+ * enabled)."
+ *
+ * From the Haswell PRM, volume 2d, page 278:
+ *
+ * "When using cube map texture coordinates, each TC component must
+ * have the same Address Control Mode.
+ *
+ * When TEXCOORDMODE_CUBE is not used accessing a cube map, the map's
+ * Cube Face Enable field must be programmed to 111111b (all faces
+ * enabled)."
+ *
+ * We always enable all cube faces and only need to make sure all address
+ * control modes are the same.
+ */
+ const enum gen_texcoord_mode tcx_ctrl =
+ (ilo_dev_gen(dev) >= ILO_GEN(7.5) ||
+ info->tcx_ctrl == GEN6_TEXCOORDMODE_CUBE ||
+ info->tcx_ctrl == GEN6_TEXCOORDMODE_CLAMP) ?
+ info->tcx_ctrl : GEN6_TEXCOORDMODE_CLAMP;
+ const enum gen_texcoord_mode tcy_ctrl = tcx_ctrl;
+ const enum gen_texcoord_mode tcz_ctrl = tcx_ctrl;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ return get_gen6_addr_controls(dev, tcx_ctrl, tcy_ctrl, tcz_ctrl);
+}
+
+static uint16_t
+get_gen6_lod_bias(const struct ilo_dev *dev, float bias)
+{
+ /* [-16.0, 16.0) in S4.6 or S4.8 */
+ const int fbits = (ilo_dev_gen(dev) >= ILO_GEN(7)) ? 8 : 6;
+ const float max = 16.0f;
+ const float scale = (float) (1 << fbits);
+ const int mask = (1 << (1 + 4 + fbits)) - 1;
+ const int scaled_max = (16 << fbits) - 1;
+ int scaled;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ if (bias > max)
+ bias = max;
+ else if (bias < -max)
+ bias = -max;
+
+ scaled = (int) (bias * scale);
+ if (scaled > scaled_max)
+ scaled = scaled_max;
+
+ return (scaled & mask);
+}
+
+static uint16_t
+get_gen6_lod_clamp(const struct ilo_dev *dev, float clamp)
+{
+ /* [0.0, 13.0] in U4.6 or [0.0, 14.0] in U4.8 */
+ const int fbits = (ilo_dev_gen(dev) >= ILO_GEN(7)) ? 8 : 6;
+ const float max = (ilo_dev_gen(dev) >= ILO_GEN(7)) ? 14.0f : 13.0f;
+ const float scale = (float) (1 << fbits);
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ if (clamp > max)
+ clamp = max;
+ else if (clamp < 0.0f)
+ clamp = 0.0f;
+
+ return (int) (clamp * scale);
+}
+
+static bool
+sampler_set_gen6_SAMPLER_STATE(struct ilo_state_sampler *sampler,
+ const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ uint16_t lod_bias, max_lod, min_lod;
+ uint32_t dw0, dw1, dw3;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ if (!sampler_validate_gen6_sampler(dev, info))
+ return false;
+
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 15:
+ *
+ * "The per-pixel LOD is computed in an implementation-dependent manner
+ * and approximates the log2 of the texel/pixel ratio at the given
+ * pixel. The computation is typically based on the differential
+ * texel-space distances associated with a one-pixel differential
+ * distance along the screen x- and y-axes. These texel-space
+ * distances are computed by evaluating neighboring pixel texture
+ * coordinates, these coordinates being in units of texels on the base
+ * MIP level (multiplied by the corresponding surface size in
+ * texels)."
+ *
+ * Judging from the LOD computation pseudocode on page 16-18, the "base MIP
+ * level" should be given by SurfMinLod. To summarize, for the "sample"
+ * message,
+ *
+ * 1) LOD is set to log2(texel/pixel ratio). The number of texels is
+ * measured against level SurfMinLod.
+ * 2) Bias is added to LOD.
+ * 3) if pre-clamp is enabled, LOD is clamped to [MinLod, MaxLod] first
+ * 4) LOD is compared with Base to determine whether magnification or
+ * minification is needed.
+ * 5) If magnification is needed, or no mipmapping is requested, LOD is
+ * set to floor(MinLod).
+ * 6) LOD is clamped to [0, MIPCnt], and SurfMinLod is added to LOD.
+ *
+ * As an example, we could set SurfMinLod to GL_TEXTURE_BASE_LEVEL and Base
+ * to 0 to match GL. But GL expects LOD to be set to 0, instead of
+ * floor(MinLod), in 5). Since this is only an issue when MinLod is
+ * greater than or equal to one, and, with Base being 0, a non-zero MinLod
+ * implies minification, we only need to deal with the case when mipmapping
+ * is disabled. We can thus do:
+ *
+ * if (MipFilter == MIPFILTER_NONE && MinLod) {
+ * MinLod = 0;
+ * MagFilter = MinFilter;
+ * }
+ */
+
+ lod_bias = get_gen6_lod_bias(dev, info->lod_bias);
+ min_lod = get_gen6_lod_clamp(dev, info->min_lod);
+ max_lod = get_gen6_lod_clamp(dev, info->max_lod);
+
+ dw0 = GEN6_SAMPLER_DW0_LOD_PRECLAMP_ENABLE |
+ 0 << GEN6_SAMPLER_DW0_BASE_LOD__SHIFT |
+ info->mip_filter << GEN6_SAMPLER_DW0_MIP_FILTER__SHIFT |
+ info->mag_filter << GEN6_SAMPLER_DW0_MAG_FILTER__SHIFT |
+ info->min_filter << GEN6_SAMPLER_DW0_MIN_FILTER__SHIFT;
+
+ if (ilo_dev_gen(dev) >= ILO_GEN(7)) {
+ dw0 |= GEN7_SAMPLER_DW0_BORDER_COLOR_MODE_DX10_OGL |
+ lod_bias << GEN7_SAMPLER_DW0_LOD_BIAS__SHIFT;
+
+ if (info->min_filter == GEN6_MAPFILTER_ANISOTROPIC ||
+ info->mag_filter == GEN6_MAPFILTER_ANISOTROPIC)
+ dw0 |= GEN7_SAMPLER_DW0_ANISO_ALGO_EWA;
+ } else {
+ dw0 |= lod_bias << GEN6_SAMPLER_DW0_LOD_BIAS__SHIFT |
+ info->shadow_func << GEN6_SAMPLER_DW0_SHADOW_FUNC__SHIFT;
+
+ /*
+ * From the Sandy Bridge PRM, volume 4 part 1, page 102:
+ *
+ * "(Min and Mag State Not Equal) Must be set to 1 if any of the
+ * following are true:
+ *
+ * - Mag Mode Filter and Min Mode Filter are not the same
+ * - Address Rounding Enable: U address mag filter and U address
+ * min filter are not the same
+ * - Address Rounding Enable: V address mag filter and V address
+ * min filter are not the same
+ * - Address Rounding Enable: R address mag filter and R address
+ * min filter are not the same"
+ *
+ * We set address rounding for U, V, and R uniformly. Only need to
+ * check the filters.
+ */
+ if (info->min_filter != info->mag_filter)
+ dw0 |= GEN6_SAMPLER_DW0_MIN_MAG_NOT_EQUAL;
+ }
+
+ dw1 = 0;
+
+ if (ilo_dev_gen(dev) >= ILO_GEN(7)) {
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 96:
+ *
+ * "This field (Cube Surface Control Mode) must be set to
+ * CUBECTRLMODE_PROGRAMMED"
+ */
+ dw1 |= min_lod << GEN7_SAMPLER_DW1_MIN_LOD__SHIFT |
+ max_lod << GEN7_SAMPLER_DW1_MAX_LOD__SHIFT |
+ info->shadow_func << GEN7_SAMPLER_DW1_SHADOW_FUNC__SHIFT |
+ GEN7_SAMPLER_DW1_CUBECTRLMODE_PROGRAMMED;
+ } else {
+ dw1 |= min_lod << GEN6_SAMPLER_DW1_MIN_LOD__SHIFT |
+ max_lod << GEN6_SAMPLER_DW1_MAX_LOD__SHIFT |
+ GEN6_SAMPLER_DW1_CUBECTRLMODE_PROGRAMMED |
+ info->tcx_ctrl << GEN6_SAMPLER_DW1_U_WRAP__SHIFT |
+ info->tcy_ctrl << GEN6_SAMPLER_DW1_V_WRAP__SHIFT |
+ info->tcz_ctrl << GEN6_SAMPLER_DW1_R_WRAP__SHIFT;
+ }
+
+ dw3 = info->max_anisotropy << GEN6_SAMPLER_DW3_MAX_ANISO__SHIFT;
+
+ /* round the coordinates for linear filtering */
+ if (info->min_filter != GEN6_MAPFILTER_NEAREST) {
+ dw3 |= GEN6_SAMPLER_DW3_U_MIN_ROUND |
+ GEN6_SAMPLER_DW3_V_MIN_ROUND |
+ GEN6_SAMPLER_DW3_R_MIN_ROUND;
+ }
+ if (info->mag_filter != GEN6_MAPFILTER_NEAREST) {
+ dw3 |= GEN6_SAMPLER_DW3_U_MAG_ROUND |
+ GEN6_SAMPLER_DW3_V_MAG_ROUND |
+ GEN6_SAMPLER_DW3_R_MAG_ROUND;
+ }
+
+ if (ilo_dev_gen(dev) >= ILO_GEN(7)) {
+ dw3 |= GEN7_SAMPLER_DW3_TRIQUAL_FULL |
+ info->tcx_ctrl << GEN7_SAMPLER_DW3_U_WRAP__SHIFT |
+ info->tcy_ctrl << GEN7_SAMPLER_DW3_V_WRAP__SHIFT |
+ info->tcz_ctrl << GEN7_SAMPLER_DW3_R_WRAP__SHIFT;
+
+ if (info->non_normalized)
+ dw3 |= GEN7_SAMPLER_DW3_NON_NORMALIZED_COORD;
+ } else {
+ if (info->non_normalized)
+ dw3 |= GEN6_SAMPLER_DW3_NON_NORMALIZED_COORD;
+ }
+
+ STATIC_ASSERT(ARRAY_SIZE(sampler->sampler) >= 3);
+ sampler->sampler[0] = dw0;
+ sampler->sampler[1] = dw1;
+ sampler->sampler[2] = dw3;
+
+ sampler->filter_integer = sampler_get_gen6_integer_filters(dev, info);
+ sampler->filter_3d = sampler_get_gen6_3d_filters(dev, info);
+ sampler->addr_ctrl_1d = sampler_get_gen6_1d_addr_controls(dev, info);
+ sampler->addr_ctrl_2d_3d = sampler_get_gen6_2d_3d_addr_controls(dev, info);
+ sampler->addr_ctrl_cube = sampler_get_gen6_cube_addr_controls(dev, info);
+
+ sampler->non_normalized = info->non_normalized;
+
+ /*
+ * From the Sandy Bridge PRM, volume 4 part 1, page 21:
+ *
+ * "[DevSNB] Errata: Incorrect behavior is observed in cases where the
+ * min and mag mode filters are different and SurfMinLOD is nonzero.
+ * The determination of MagMode uses the following equation instead of
+ * the one in the above pseudocode:
+ *
+ * MagMode = (LOD + SurfMinLOD - Base <= 0)"
+ *
+ * As a way to work around that, request Base to be set to SurfMinLod.
+ */
+ if (ilo_dev_gen(dev) == ILO_GEN(6) &&
+ info->min_filter != info->mag_filter)
+ sampler->base_to_surf_min_lod = true;
+
+ return true;
+}
+
+static bool
+sampler_border_set_gen6_SAMPLER_BORDER_COLOR_STATE(struct ilo_state_sampler_border *border,
+ const struct ilo_dev *dev,
+ const struct ilo_state_sampler_border_info *info)
+{
+ uint32_t dw[12];
+ float rgba[4];
+
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 117:
+ *
+ * "For ([DevSNB]), if border color is used, all formats must be
+ * provided. Hardware will choose the appropriate format based on
+ * Surface Format and Texture Border Color Mode. The values
+ * represented by each format should be the same (other than being
+ * subject to range-based clamping and precision) to avoid unexpected
+ * behavior."
+ *
+ * XXX We do not honor info->is_integer yet.
+ */
+
+ ILO_DEV_ASSERT(dev, 6, 6);
+
+ /* make a copy so that we can clamp for SNORM and UNORM */
+ memcpy(rgba, info->rgba.f, sizeof(rgba));
+
+ /* IEEE_FP */
+ dw[1] = fui(rgba[0]);
+ dw[2] = fui(rgba[1]);
+ dw[3] = fui(rgba[2]);
+ dw[4] = fui(rgba[3]);
+
+ /* FLOAT_16 */
+ dw[5] = util_float_to_half(rgba[0]) |
+ util_float_to_half(rgba[1]) << 16;
+ dw[6] = util_float_to_half(rgba[2]) |
+ util_float_to_half(rgba[3]) << 16;
+
+ /* clamp to [-1.0f, 1.0f] */
+ rgba[0] = CLAMP(rgba[0], -1.0f, 1.0f);
+ rgba[1] = CLAMP(rgba[1], -1.0f, 1.0f);
+ rgba[2] = CLAMP(rgba[2], -1.0f, 1.0f);
+ rgba[3] = CLAMP(rgba[3], -1.0f, 1.0f);
+
+ /* SNORM16 */
+ dw[9] = (int16_t) util_iround(rgba[0] * 32767.0f) |
+ (int16_t) util_iround(rgba[1] * 32767.0f) << 16;
+ dw[10] = (int16_t) util_iround(rgba[2] * 32767.0f) |
+ (int16_t) util_iround(rgba[3] * 32767.0f) << 16;
+
+ /* SNORM8 */
+ dw[11] = (int8_t) util_iround(rgba[0] * 127.0f) |
+ (int8_t) util_iround(rgba[1] * 127.0f) << 8 |
+ (int8_t) util_iround(rgba[2] * 127.0f) << 16 |
+ (int8_t) util_iround(rgba[3] * 127.0f) << 24;
+
+ /* clamp to [0.0f, 1.0f] */
+ rgba[0] = CLAMP(rgba[0], 0.0f, 1.0f);
+ rgba[1] = CLAMP(rgba[1], 0.0f, 1.0f);
+ rgba[2] = CLAMP(rgba[2], 0.0f, 1.0f);
+ rgba[3] = CLAMP(rgba[3], 0.0f, 1.0f);
+
+ /* UNORM8 */
+ dw[0] = (uint8_t) util_iround(rgba[0] * 255.0f) |
+ (uint8_t) util_iround(rgba[1] * 255.0f) << 8 |
+ (uint8_t) util_iround(rgba[2] * 255.0f) << 16 |
+ (uint8_t) util_iround(rgba[3] * 255.0f) << 24;
+
+ /* UNORM16 */
+ dw[7] = (uint16_t) util_iround(rgba[0] * 65535.0f) |
+ (uint16_t) util_iround(rgba[1] * 65535.0f) << 16;
+ dw[8] = (uint16_t) util_iround(rgba[2] * 65535.0f) |
+ (uint16_t) util_iround(rgba[3] * 65535.0f) << 16;
+
+ STATIC_ASSERT(ARRAY_SIZE(border->color) >= 12);
+ memcpy(border->color, dw, sizeof(dw));
+
+ return true;
+}
+
+static bool
+sampler_border_set_gen7_SAMPLER_BORDER_COLOR_STATE(struct ilo_state_sampler_border *border,
+ const struct ilo_dev *dev,
+ const struct ilo_state_sampler_border_info *info)
+{
+ ILO_DEV_ASSERT(dev, 7, 8);
+
+ /*
+ * From the Ivy Bridge PRM, volume 4 part 1, page 116:
+ *
+ * "In DX10/OGL mode, the format of the border color is
+ * R32G32B32A32_FLOAT, regardless of the surface format chosen."
+ *
+ * From the Haswell PRM, volume 2d, page 240:
+ *
+ * "So, SW will have to program the table in SAMPLER_BORDER_COLOR_STATE
+ * at offsets DWORD16 to 19, as per the integer surface format type."
+ *
+ * From the Broadwell PRM, volume 2d, page 297:
+ *
+ * "DX10/OGL mode: the format of the border color depends on the format
+ * of the surface being sampled. If the map format is UINT, then the
+ * border color format is R32G32B32A32_UINT. If the map format is
+ * SINT, then the border color format is R32G32B32A32_SINT. Otherwise,
+ * the border color format is R32G32B32A32_FLOAT."
+ *
+ * XXX every Gen is different
+ */
+
+ STATIC_ASSERT(ARRAY_SIZE(border->color) >= 4);
+ memcpy(border->color, info->rgba.f, sizeof(info->rgba.f));
+
+ return true;
+}
+
+bool
+ilo_state_sampler_init(struct ilo_state_sampler *sampler,
+ const struct ilo_dev *dev,
+ const struct ilo_state_sampler_info *info)
+{
+ bool ret = true;
+
+ assert(ilo_is_zeroed(sampler, sizeof(*sampler)));
+
+ ret &= sampler_set_gen6_SAMPLER_STATE(sampler, dev, info);
+
+ assert(ret);
+
+ return ret;
+}
+
+bool
+ilo_state_sampler_init_disabled(struct ilo_state_sampler *sampler,
+ const struct ilo_dev *dev)
+{
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ assert(ilo_is_zeroed(sampler, sizeof(*sampler)));
+
+ sampler->sampler[0] = GEN6_SAMPLER_DW0_DISABLE;
+ sampler->sampler[1] = 0;
+ sampler->sampler[2] = 0;
+
+ return true;
+}
+
+/**
+ * Modify \p sampler to work with \p surf. There will be loss of information.
+ * Callers should make a copy of the orignal sampler first.
+ */
+bool
+ilo_state_sampler_set_surface(struct ilo_state_sampler *sampler,
+ const struct ilo_dev *dev,
+ const struct ilo_state_surface *surf)
+{
+ uint32_t addr_ctrl;
+
+ ILO_DEV_ASSERT(dev, 6, 8);
+
+ if (sampler->non_normalized) {
+ /* see sampler_validate_gen6_non_normalized() */
+ assert(surf->type == GEN6_SURFTYPE_2D ||
+ surf->type == GEN6_SURFTYPE_3D);
+ assert(!surf->min_lod && !surf->mip_count);
+ }
+
+ if (sampler->base_to_surf_min_lod) {
+ const uint8_t base = surf->min_lod << GEN6_SAMPLER_DW0_BASE_LOD__RADIX;
+
+ sampler->sampler[0] =
+ (sampler->sampler[0] & ~GEN6_SAMPLER_DW0_BASE_LOD__MASK) |
+ base << GEN6_SAMPLER_DW0_BASE_LOD__SHIFT;
+ }
+
+ if (surf->is_integer || surf->type == GEN6_SURFTYPE_3D) {
+ const uint32_t mask = (GEN6_SAMPLER_DW0_MIP_FILTER__MASK |
+ GEN6_SAMPLER_DW0_MIN_FILTER__MASK |
+ GEN6_SAMPLER_DW0_MAG_FILTER__MASK);
+ const uint32_t filter = (surf->is_integer) ?
+ sampler->filter_integer : sampler->filter_3d;
+
+ assert((filter & mask) == filter);
+ sampler->sampler[0] = (sampler->sampler[0] & ~mask) |
+ filter;
+ }
+
+ switch (surf->type) {
+ case GEN6_SURFTYPE_1D:
+ addr_ctrl = sampler->addr_ctrl_1d;
+ break;
+ case GEN6_SURFTYPE_2D:
+ case GEN6_SURFTYPE_3D:
+ addr_ctrl = sampler->addr_ctrl_2d_3d;
+ break;
+ case GEN6_SURFTYPE_CUBE:
+ addr_ctrl = sampler->addr_ctrl_cube;
+ break;
+ default:
+ assert(!"unexpected surface type");
+ addr_ctrl = 0;
+ break;
+ }
+
+ if (ilo_dev_gen(dev) >= ILO_GEN(7)) {
+ const uint32_t mask = (GEN7_SAMPLER_DW3_U_WRAP__MASK |
+ GEN7_SAMPLER_DW3_V_WRAP__MASK |
+ GEN7_SAMPLER_DW3_R_WRAP__MASK);
+
+ assert((addr_ctrl & mask) == addr_ctrl);
+ sampler->sampler[2] = (sampler->sampler[2] & ~mask) |
+ addr_ctrl;
+ } else {
+ const uint32_t mask = (GEN6_SAMPLER_DW1_U_WRAP__MASK |
+ GEN6_SAMPLER_DW1_V_WRAP__MASK |
+ GEN6_SAMPLER_DW1_R_WRAP__MASK);
+
+ assert((addr_ctrl & mask) == addr_ctrl);
+ sampler->sampler[1] = (sampler->sampler[1] & ~mask) |
+ addr_ctrl;
+ }
+
+ return true;
+}
+
+bool
+ilo_state_sampler_border_init(struct ilo_state_sampler_border *border,
+ const struct ilo_dev *dev,
+ const struct ilo_state_sampler_border_info *info)
+{
+ bool ret = true;
+
+ if (ilo_dev_gen(dev) >= ILO_GEN(7)) {
+ ret &= sampler_border_set_gen7_SAMPLER_BORDER_COLOR_STATE(border,
+ dev, info);
+ } else {
+ ret &= sampler_border_set_gen6_SAMPLER_BORDER_COLOR_STATE(border,
+ dev, info);
+ }
+
+ assert(ret);
+
+ return ret;
+}