Texture.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. /*
  2. * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
  3. * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
  4. * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <LibGL/GLContext.h>
  9. #include <LibGL/Image.h>
  10. #include <LibGPU/ImageDataLayout.h>
  11. namespace GL {
  12. // Helper functions to handle type casting.
  13. static u16 max_texture_size(GPU::DeviceInfo const& device_info)
  14. {
  15. return static_cast<u16>(device_info.max_texture_size);
  16. }
  17. static u8 log2_max_texture_size(GPU::DeviceInfo const& device_info)
  18. {
  19. return static_cast<u8>(AK::log2(device_info.max_texture_size));
  20. }
  21. void GLContext::gl_active_texture(GLenum texture)
  22. {
  23. RETURN_WITH_ERROR_IF(texture < GL_TEXTURE0 || texture >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
  24. m_active_texture_unit_index = texture - GL_TEXTURE0;
  25. m_active_texture_unit = &m_texture_units.at(m_active_texture_unit_index);
  26. if (m_current_matrix_mode == GL_TEXTURE) {
  27. m_current_matrix_stack = &m_active_texture_unit->texture_matrix_stack();
  28. m_current_matrix = &m_current_matrix_stack->last();
  29. }
  30. }
  31. void GLContext::gl_bind_texture(GLenum target, GLuint texture)
  32. {
  33. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  34. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_1D
  35. && target != GL_TEXTURE_2D
  36. && target != GL_TEXTURE_3D
  37. && target != GL_TEXTURE_1D_ARRAY
  38. && target != GL_TEXTURE_2D_ARRAY
  39. && target != GL_TEXTURE_CUBE_MAP,
  40. GL_INVALID_ENUM);
  41. // FIXME: We only support GL_TEXTURE_2D for now
  42. if (target != GL_TEXTURE_2D) {
  43. dbgln("gl_bind_texture(target = {:#x}): currently only GL_TEXTURE_2D is supported", target);
  44. return;
  45. }
  46. RefPtr<Texture2D> texture_2d;
  47. if (texture == 0) {
  48. // Texture name 0 refers to the default texture
  49. texture_2d = get_default_texture<Texture2D>(target);
  50. } else {
  51. // Find this texture name in our previously allocated textures
  52. auto it = m_allocated_textures.find(texture);
  53. if (it != m_allocated_textures.end()) {
  54. auto texture_object = it->value;
  55. if (!texture_object.is_null()) {
  56. // Texture must have been created with the same target
  57. RETURN_WITH_ERROR_IF(!texture_object->is_texture_2d(), GL_INVALID_OPERATION);
  58. texture_2d = static_cast<Texture2D*>(texture_object.ptr());
  59. }
  60. }
  61. // OpenGL 1.x supports binding texture names that were not previously generated by glGenTextures.
  62. // If there is not an allocated texture, meaning it was not previously generated by glGenTextures,
  63. // we can keep texture_object null to both allocate and bind the texture with the passed in texture name.
  64. // FIXME: Later OpenGL versions such as 4.x enforce that texture names being bound were previously generated
  65. // by glGenTextures.
  66. if (!texture_2d) {
  67. texture_2d = adopt_ref(*new Texture2D());
  68. m_allocated_textures.set(texture, texture_2d);
  69. }
  70. }
  71. m_active_texture_unit->set_texture_2d_target_texture(texture_2d);
  72. m_sampler_config_is_dirty = true;
  73. }
  74. void GLContext::gl_client_active_texture(GLenum target)
  75. {
  76. RETURN_WITH_ERROR_IF(target < GL_TEXTURE0 || target >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
  77. m_client_active_texture = target - GL_TEXTURE0;
  78. }
  79. void GLContext::gl_copy_tex_image_2d(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
  80. {
  81. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_copy_tex_image_2d, target, level, internalformat, x, y, width, height, border);
  82. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  83. RETURN_WITH_ERROR_IF(internalformat == GL_NONE, GL_INVALID_ENUM);
  84. auto pixel_type_or_error = get_validated_pixel_type(target, internalformat, GL_NONE, GL_NONE);
  85. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  86. RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
  87. RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
  88. if (!m_device_info.supports_npot_textures)
  89. RETURN_WITH_ERROR_IF(!is_power_of_two(width) || !is_power_of_two(height), GL_INVALID_VALUE);
  90. RETURN_WITH_ERROR_IF(border != 0, GL_INVALID_VALUE);
  91. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  92. VERIFY(!texture_2d.is_null());
  93. auto internal_pixel_format = pixel_format_for_internal_format(internalformat);
  94. if (level == 0) {
  95. texture_2d->set_device_image(m_rasterizer->create_image(internal_pixel_format, width, height, 1, log2_max_texture_size(m_device_info)));
  96. m_sampler_config_is_dirty = true;
  97. }
  98. auto pixel_type = pixel_type_or_error.release_value();
  99. if (pixel_type.format == GPU::PixelFormat::DepthComponent) {
  100. m_rasterizer->blit_from_depth_buffer(
  101. *texture_2d->device_image(),
  102. level,
  103. { static_cast<u32>(width), static_cast<u32>(height) },
  104. { x, y },
  105. { 0, 0, 0 });
  106. } else if (pixel_type.format == GPU::PixelFormat::StencilIndex) {
  107. dbgln("{}: GL_STENCIL_INDEX is not yet supported", __FUNCTION__);
  108. } else {
  109. m_rasterizer->blit_from_color_buffer(
  110. *texture_2d->device_image(),
  111. level,
  112. { static_cast<u32>(width), static_cast<u32>(height) },
  113. { x, y },
  114. { 0, 0, 0 });
  115. }
  116. }
  117. void GLContext::gl_copy_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
  118. {
  119. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_copy_tex_sub_image_2d, target, level, xoffset, yoffset, x, y, width, height);
  120. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  121. RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
  122. RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
  123. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  124. VERIFY(!texture_2d.is_null());
  125. RETURN_WITH_ERROR_IF(texture_2d->device_image().is_null(), GL_INVALID_OPERATION);
  126. m_rasterizer->blit_from_color_buffer(
  127. *texture_2d->device_image(),
  128. level,
  129. { static_cast<u32>(width), static_cast<u32>(height) },
  130. { x, y },
  131. { xoffset, yoffset, 0 });
  132. // FIXME: use GPU::PixelFormat for Texture2D's internal format
  133. if (texture_2d->internal_format() == GL_DEPTH_COMPONENT) {
  134. m_rasterizer->blit_from_depth_buffer(
  135. *texture_2d->device_image(),
  136. level,
  137. { static_cast<u32>(width), static_cast<u32>(height) },
  138. { x, y },
  139. { 0, 0, 0 });
  140. } else if (texture_2d->internal_format() == GL_STENCIL_INDEX) {
  141. dbgln("{}: GL_STENCIL_INDEX is not yet supported", __FUNCTION__);
  142. } else {
  143. m_rasterizer->blit_from_color_buffer(
  144. *texture_2d->device_image(),
  145. level,
  146. { static_cast<u32>(width), static_cast<u32>(height) },
  147. { x, y },
  148. { 0, 0, 0 });
  149. }
  150. }
  151. void GLContext::gl_delete_textures(GLsizei n, GLuint const* textures)
  152. {
  153. RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
  154. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  155. for (auto i = 0; i < n; i++) {
  156. GLuint name = textures[i];
  157. if (name == 0)
  158. continue;
  159. auto texture_object = m_allocated_textures.find(name);
  160. if (texture_object == m_allocated_textures.end() || texture_object->value.is_null())
  161. continue;
  162. m_texture_name_allocator.free(name);
  163. auto texture = texture_object->value;
  164. // Check all texture units
  165. for (auto& texture_unit : m_texture_units) {
  166. if (texture->is_texture_2d() && texture_unit.texture_2d_target_texture() == texture) {
  167. // If a texture that is currently bound is deleted, the binding reverts to 0 (the default texture)
  168. texture_unit.set_texture_2d_target_texture(get_default_texture<Texture2D>(GL_TEXTURE_2D));
  169. }
  170. }
  171. m_allocated_textures.remove(name);
  172. }
  173. }
  174. void GLContext::gl_gen_textures(GLsizei n, GLuint* textures)
  175. {
  176. RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
  177. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  178. m_texture_name_allocator.allocate(n, textures);
  179. // Initialize all texture names with a nullptr
  180. for (auto i = 0; i < n; ++i) {
  181. GLuint name = textures[i];
  182. m_allocated_textures.set(name, nullptr);
  183. }
  184. }
  185. void GLContext::gl_get_tex_image(GLenum target, GLint level, GLenum format, GLenum type, void* pixels)
  186. {
  187. RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
  188. RETURN_WITH_ERROR_IF(format == GL_NONE || type == GL_NONE, GL_INVALID_ENUM);
  189. auto pixel_type_or_error = get_validated_pixel_type(target, GL_NONE, format, type);
  190. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  191. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  192. VERIFY(!texture_2d.is_null());
  193. u32 width = texture_2d->width_at_lod(level);
  194. u32 height = texture_2d->height_at_lod(level);
  195. GPU::ImageDataLayout output_layout = {
  196. .pixel_type = pixel_type_or_error.release_value(),
  197. .packing = get_packing_specification(PackingType::Pack),
  198. .dimensions = {
  199. .width = width,
  200. .height = height,
  201. .depth = 1,
  202. },
  203. .selection = {
  204. .width = width,
  205. .height = height,
  206. .depth = 1,
  207. },
  208. };
  209. texture_2d->download_texture_data(level, output_layout, pixels);
  210. }
  211. void GLContext::gl_get_tex_parameter_integerv(GLenum target, GLint level, GLenum pname, GLint* params)
  212. {
  213. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  214. // FIXME: support targets other than GL_TEXTURE_2D
  215. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
  216. // FIXME: support other parameter names
  217. RETURN_WITH_ERROR_IF(pname < GL_TEXTURE_WIDTH || pname > GL_TEXTURE_HEIGHT, GL_INVALID_ENUM);
  218. RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
  219. // FIXME: GL_INVALID_VALUE is generated if target is GL_TEXTURE_BUFFER and level is not zero
  220. // FIXME: GL_INVALID_OPERATION is generated if GL_TEXTURE_COMPRESSED_IMAGE_SIZE is queried on texture images with an uncompressed internal format or on proxy targets
  221. VERIFY(!m_active_texture_unit->texture_2d_target_texture().is_null());
  222. auto const texture_2d = m_active_texture_unit->texture_2d_target_texture();
  223. switch (pname) {
  224. case GL_TEXTURE_HEIGHT:
  225. *params = texture_2d->height_at_lod(level);
  226. break;
  227. case GL_TEXTURE_WIDTH:
  228. *params = texture_2d->width_at_lod(level);
  229. break;
  230. }
  231. }
  232. GLboolean GLContext::gl_is_texture(GLuint texture)
  233. {
  234. RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, GL_FALSE);
  235. if (texture == 0)
  236. return GL_FALSE;
  237. auto it = m_allocated_textures.find(texture);
  238. if (it == m_allocated_textures.end())
  239. return GL_FALSE;
  240. return it->value.is_null() ? GL_FALSE : GL_TRUE;
  241. }
  242. void GLContext::gl_multi_tex_coord(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
  243. {
  244. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_multi_tex_coord, target, s, t, r, q);
  245. RETURN_WITH_ERROR_IF(target < GL_TEXTURE0 || target >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
  246. m_current_vertex_tex_coord[target - GL_TEXTURE0] = { s, t, r, q };
  247. }
  248. void GLContext::gl_tex_coord(GLfloat s, GLfloat t, GLfloat r, GLfloat q)
  249. {
  250. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_coord, s, t, r, q);
  251. m_current_vertex_tex_coord[0] = { s, t, r, q };
  252. }
  253. void GLContext::gl_tex_env(GLenum target, GLenum pname, FloatVector4 params)
  254. {
  255. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_env, target, pname, params);
  256. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  257. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_ENV && target != GL_TEXTURE_FILTER_CONTROL, GL_INVALID_ENUM);
  258. RETURN_WITH_ERROR_IF(target == GL_TEXTURE_FILTER_CONTROL && pname != GL_TEXTURE_LOD_BIAS, GL_INVALID_ENUM);
  259. auto const param = params[0];
  260. switch (target) {
  261. case GL_TEXTURE_ENV:
  262. switch (pname) {
  263. case GL_ALPHA_SCALE:
  264. RETURN_WITH_ERROR_IF(param != 1.f && param != 2.f && param != 4.f, GL_INVALID_VALUE);
  265. m_active_texture_unit->set_alpha_scale(param);
  266. break;
  267. case GL_COMBINE_ALPHA: {
  268. auto param_enum = static_cast<GLenum>(param);
  269. switch (param_enum) {
  270. case GL_ADD:
  271. case GL_ADD_SIGNED:
  272. case GL_INTERPOLATE:
  273. case GL_MODULATE:
  274. case GL_REPLACE:
  275. case GL_SUBTRACT:
  276. m_active_texture_unit->set_alpha_combinator(param_enum);
  277. break;
  278. default:
  279. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  280. }
  281. break;
  282. }
  283. case GL_COMBINE_RGB: {
  284. auto param_enum = static_cast<GLenum>(param);
  285. switch (param_enum) {
  286. case GL_ADD:
  287. case GL_ADD_SIGNED:
  288. case GL_DOT3_RGB:
  289. case GL_DOT3_RGBA:
  290. case GL_INTERPOLATE:
  291. case GL_MODULATE:
  292. case GL_REPLACE:
  293. case GL_SUBTRACT:
  294. m_active_texture_unit->set_rgb_combinator(param_enum);
  295. break;
  296. default:
  297. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  298. }
  299. break;
  300. }
  301. case GL_OPERAND0_ALPHA:
  302. case GL_OPERAND1_ALPHA:
  303. case GL_OPERAND2_ALPHA: {
  304. auto param_enum = static_cast<GLenum>(param);
  305. switch (param_enum) {
  306. case GL_ONE_MINUS_SRC_ALPHA:
  307. case GL_SRC_ALPHA:
  308. m_active_texture_unit->set_alpha_operand(pname - GL_OPERAND0_ALPHA, param_enum);
  309. break;
  310. default:
  311. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  312. }
  313. break;
  314. }
  315. case GL_OPERAND0_RGB:
  316. case GL_OPERAND1_RGB:
  317. case GL_OPERAND2_RGB: {
  318. auto param_enum = static_cast<GLenum>(param);
  319. switch (param_enum) {
  320. case GL_ONE_MINUS_SRC_ALPHA:
  321. case GL_ONE_MINUS_SRC_COLOR:
  322. case GL_SRC_ALPHA:
  323. case GL_SRC_COLOR:
  324. m_active_texture_unit->set_rgb_operand(pname - GL_OPERAND0_RGB, param_enum);
  325. break;
  326. default:
  327. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  328. }
  329. break;
  330. }
  331. case GL_RGB_SCALE:
  332. RETURN_WITH_ERROR_IF(param != 1.f && param != 2.f && param != 4.f, GL_INVALID_VALUE);
  333. m_active_texture_unit->set_rgb_scale(param);
  334. break;
  335. case GL_SRC0_ALPHA:
  336. case GL_SRC1_ALPHA:
  337. case GL_SRC2_ALPHA: {
  338. auto param_enum = static_cast<GLenum>(param);
  339. switch (param_enum) {
  340. case GL_CONSTANT:
  341. case GL_PREVIOUS:
  342. case GL_PRIMARY_COLOR:
  343. case GL_TEXTURE:
  344. case GL_TEXTURE0 ... GL_TEXTURE31:
  345. m_active_texture_unit->set_alpha_source(pname - GL_SRC0_ALPHA, param_enum);
  346. break;
  347. default:
  348. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  349. }
  350. break;
  351. }
  352. case GL_SRC0_RGB:
  353. case GL_SRC1_RGB:
  354. case GL_SRC2_RGB: {
  355. auto param_enum = static_cast<GLenum>(param);
  356. switch (param_enum) {
  357. case GL_CONSTANT:
  358. case GL_PREVIOUS:
  359. case GL_PRIMARY_COLOR:
  360. case GL_TEXTURE:
  361. case GL_TEXTURE0 ... GL_TEXTURE31:
  362. m_active_texture_unit->set_rgb_source(pname - GL_SRC0_RGB, param_enum);
  363. break;
  364. default:
  365. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  366. }
  367. break;
  368. }
  369. case GL_TEXTURE_ENV_COLOR:
  370. m_active_texture_unit->set_color(params);
  371. break;
  372. case GL_TEXTURE_ENV_MODE: {
  373. auto param_enum = static_cast<GLenum>(param);
  374. switch (param_enum) {
  375. case GL_ADD:
  376. case GL_BLEND:
  377. case GL_COMBINE:
  378. case GL_DECAL:
  379. case GL_MODULATE:
  380. case GL_REPLACE:
  381. m_active_texture_unit->set_env_mode(param_enum);
  382. break;
  383. default:
  384. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  385. }
  386. break;
  387. }
  388. default:
  389. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  390. }
  391. break;
  392. case GL_TEXTURE_FILTER_CONTROL:
  393. switch (pname) {
  394. case GL_TEXTURE_LOD_BIAS:
  395. m_active_texture_unit->set_level_of_detail_bias(param);
  396. break;
  397. default:
  398. VERIFY_NOT_REACHED();
  399. }
  400. break;
  401. default:
  402. VERIFY_NOT_REACHED();
  403. }
  404. m_sampler_config_is_dirty = true;
  405. }
  406. void GLContext::gl_tex_envv(GLenum target, GLenum pname, void const* params, GLenum type)
  407. {
  408. VERIFY(type == GL_FLOAT || type == GL_INT);
  409. auto parameters_to_vector = [&]<typename T>(T const* params) -> FloatVector4 {
  410. auto parameters = (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR)
  411. ? Vector4<T> { params[0], params[1], params[2], params[3] }
  412. : Vector4<T> { params[0], 0, 0, 0 };
  413. return parameters.template to_type<float>();
  414. };
  415. auto tex_env_parameters = (type == GL_FLOAT)
  416. ? parameters_to_vector(reinterpret_cast<GLfloat const*>(params))
  417. : parameters_to_vector(reinterpret_cast<GLint const*>(params));
  418. // Normalize integers to -1..1
  419. if (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR && type == GL_INT)
  420. tex_env_parameters = (tex_env_parameters + 2147483648.f) / 2147483647.5f - 1.f;
  421. gl_tex_env(target, pname, tex_env_parameters);
  422. }
  423. void GLContext::gl_tex_gen(GLenum coord, GLenum pname, GLint param)
  424. {
  425. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_gen, coord, pname, param);
  426. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  427. RETURN_WITH_ERROR_IF(coord < GL_S || coord > GL_Q, GL_INVALID_ENUM);
  428. RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_GEN_MODE, GL_INVALID_ENUM);
  429. RETURN_WITH_ERROR_IF(param != GL_EYE_LINEAR
  430. && param != GL_OBJECT_LINEAR
  431. && param != GL_SPHERE_MAP
  432. && param != GL_NORMAL_MAP
  433. && param != GL_REFLECTION_MAP,
  434. GL_INVALID_ENUM);
  435. RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
  436. RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
  437. GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
  438. texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
  439. m_texture_units_dirty = true;
  440. }
  441. void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params)
  442. {
  443. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_gen_floatv, coord, pname, params);
  444. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  445. RETURN_WITH_ERROR_IF(coord < GL_S || coord > GL_Q, GL_INVALID_ENUM);
  446. RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_GEN_MODE
  447. && pname != GL_OBJECT_PLANE
  448. && pname != GL_EYE_PLANE,
  449. GL_INVALID_ENUM);
  450. GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
  451. switch (pname) {
  452. case GL_TEXTURE_GEN_MODE: {
  453. auto param = static_cast<GLenum>(params[0]);
  454. RETURN_WITH_ERROR_IF(param != GL_EYE_LINEAR
  455. && param != GL_OBJECT_LINEAR
  456. && param != GL_SPHERE_MAP
  457. && param != GL_NORMAL_MAP
  458. && param != GL_REFLECTION_MAP,
  459. GL_INVALID_ENUM);
  460. RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
  461. RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
  462. texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
  463. break;
  464. }
  465. case GL_OBJECT_PLANE:
  466. texture_coordinate_generation(m_active_texture_unit_index, capability).object_plane_coefficients = { params[0], params[1], params[2], params[3] };
  467. break;
  468. case GL_EYE_PLANE: {
  469. auto const& inverse_model_view = model_view_matrix().inverse();
  470. auto input_coefficients = FloatVector4 { params[0], params[1], params[2], params[3] };
  471. // Note: we are allowed to store transformed coefficients here, according to the documentation on
  472. // `glGetTexGen`:
  473. //
  474. // "The returned values are those maintained in eye coordinates. They are not equal to the values
  475. // specified using glTexGen, unless the modelview matrix was identity when glTexGen was called."
  476. texture_coordinate_generation(m_active_texture_unit_index, capability).eye_plane_coefficients = inverse_model_view * input_coefficients;
  477. break;
  478. }
  479. default:
  480. VERIFY_NOT_REACHED();
  481. }
  482. m_texture_units_dirty = true;
  483. }
  484. void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data)
  485. {
  486. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  487. RETURN_WITH_ERROR_IF(internal_format == GL_NONE || format == GL_NONE || type == GL_NONE, GL_INVALID_ENUM);
  488. auto pixel_type_or_error = get_validated_pixel_type(target, internal_format, format, type);
  489. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  490. RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
  491. RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
  492. if (!m_device_info.supports_npot_textures)
  493. RETURN_WITH_ERROR_IF(!is_power_of_two(width) || !is_power_of_two(height), GL_INVALID_VALUE);
  494. RETURN_WITH_ERROR_IF(border != 0, GL_INVALID_VALUE);
  495. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  496. VERIFY(!texture_2d.is_null());
  497. if (level == 0) {
  498. // FIXME: OpenGL has the concept of texture and mipmap completeness. A texture has to fulfill certain criteria to be considered complete.
  499. // Trying to render while an incomplete texture is bound will result in an error.
  500. // Here we simply create a complete device image when mipmap level 0 is attached to the texture object. This has the unfortunate side effect
  501. // that constructing GL textures in any but the default mipmap order, going from level 0 upwards will cause mip levels to stay uninitialized.
  502. // To be spec compliant we should create the device image once the texture has become complete and is used for rendering the first time.
  503. // All images that were attached before the device image was created need to be stored somewhere to be used to initialize the device image once complete.
  504. auto internal_pixel_format = pixel_format_for_internal_format(internal_format);
  505. texture_2d->set_device_image(m_rasterizer->create_image(internal_pixel_format, width, height, 1, log2_max_texture_size(m_device_info)));
  506. m_sampler_config_is_dirty = true;
  507. }
  508. GPU::ImageDataLayout input_layout = {
  509. .pixel_type = pixel_type_or_error.release_value(),
  510. .packing = get_packing_specification(PackingType::Unpack),
  511. .dimensions = {
  512. .width = static_cast<u32>(width),
  513. .height = static_cast<u32>(height),
  514. .depth = 1,
  515. },
  516. .selection = {
  517. .width = static_cast<u32>(width),
  518. .height = static_cast<u32>(height),
  519. .depth = 1,
  520. },
  521. };
  522. texture_2d->upload_texture_data(level, internal_format, input_layout, data);
  523. }
  524. void GLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param)
  525. {
  526. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_parameter, target, pname, param);
  527. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  528. // FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
  529. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
  530. // FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
  531. RETURN_WITH_ERROR_IF(pname != GL_GENERATE_MIPMAP
  532. && pname != GL_TEXTURE_LOD_BIAS
  533. && pname != GL_TEXTURE_MIN_FILTER
  534. && pname != GL_TEXTURE_MAG_FILTER
  535. && pname != GL_TEXTURE_WRAP_S
  536. && pname != GL_TEXTURE_WRAP_T,
  537. GL_INVALID_ENUM);
  538. // We assume GL_TEXTURE_2D (see above)
  539. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  540. VERIFY(!texture_2d.is_null());
  541. switch (pname) {
  542. case GL_GENERATE_MIPMAP:
  543. RETURN_WITH_ERROR_IF(param != GL_TRUE && param != GL_FALSE, GL_INVALID_ENUM);
  544. texture_2d->set_generate_mipmaps(param == GL_TRUE);
  545. break;
  546. case GL_TEXTURE_LOD_BIAS:
  547. texture_2d->set_level_of_detail_bias(param);
  548. break;
  549. case GL_TEXTURE_MIN_FILTER:
  550. RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
  551. || param == GL_LINEAR
  552. || param == GL_NEAREST_MIPMAP_NEAREST
  553. || param == GL_LINEAR_MIPMAP_NEAREST
  554. || param == GL_NEAREST_MIPMAP_LINEAR
  555. || param == GL_LINEAR_MIPMAP_LINEAR),
  556. GL_INVALID_ENUM);
  557. texture_2d->sampler().set_min_filter(param);
  558. break;
  559. case GL_TEXTURE_MAG_FILTER:
  560. RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
  561. || param == GL_LINEAR),
  562. GL_INVALID_ENUM);
  563. texture_2d->sampler().set_mag_filter(param);
  564. break;
  565. case GL_TEXTURE_WRAP_S:
  566. RETURN_WITH_ERROR_IF(!(param == GL_CLAMP
  567. || param == GL_CLAMP_TO_BORDER
  568. || param == GL_CLAMP_TO_EDGE
  569. || param == GL_MIRRORED_REPEAT
  570. || param == GL_REPEAT),
  571. GL_INVALID_ENUM);
  572. texture_2d->sampler().set_wrap_s_mode(param);
  573. break;
  574. case GL_TEXTURE_WRAP_T:
  575. RETURN_WITH_ERROR_IF(!(param == GL_CLAMP
  576. || param == GL_CLAMP_TO_BORDER
  577. || param == GL_CLAMP_TO_EDGE
  578. || param == GL_MIRRORED_REPEAT
  579. || param == GL_REPEAT),
  580. GL_INVALID_ENUM);
  581. texture_2d->sampler().set_wrap_t_mode(param);
  582. break;
  583. default:
  584. VERIFY_NOT_REACHED();
  585. }
  586. m_sampler_config_is_dirty = true;
  587. }
  588. void GLContext::gl_tex_parameterfv(GLenum target, GLenum pname, GLfloat const* params)
  589. {
  590. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_parameterfv, target, pname, params);
  591. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  592. // FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
  593. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
  594. // FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
  595. RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_BORDER_COLOR, GL_INVALID_ENUM);
  596. // We assume GL_TEXTURE_2D (see above)
  597. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  598. RETURN_WITH_ERROR_IF(texture_2d.is_null(), GL_INVALID_OPERATION);
  599. switch (pname) {
  600. case GL_TEXTURE_BORDER_COLOR:
  601. texture_2d->sampler().set_border_color(params[0], params[1], params[2], params[3]);
  602. break;
  603. default:
  604. VERIFY_NOT_REACHED();
  605. }
  606. m_sampler_config_is_dirty = true;
  607. }
  608. void GLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* data)
  609. {
  610. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  611. // We only support symbolic constants for now
  612. RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
  613. RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
  614. // A 2D texture array must have been defined by a previous glTexImage2D operation
  615. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  616. VERIFY(!texture_2d.is_null());
  617. RETURN_WITH_ERROR_IF(texture_2d->device_image().is_null(), GL_INVALID_OPERATION);
  618. RETURN_WITH_ERROR_IF(format == GL_NONE || type == GL_NONE, GL_INVALID_ENUM);
  619. auto pixel_type_or_error = get_validated_pixel_type(target, texture_2d->internal_format(), format, type);
  620. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  621. RETURN_WITH_ERROR_IF(xoffset < 0 || yoffset < 0 || xoffset + width > texture_2d->width_at_lod(level) || yoffset + height > texture_2d->height_at_lod(level), GL_INVALID_VALUE);
  622. GPU::ImageDataLayout input_layout = {
  623. .pixel_type = pixel_type_or_error.release_value(),
  624. .packing = get_packing_specification(PackingType::Unpack),
  625. .dimensions = {
  626. .width = static_cast<u32>(width),
  627. .height = static_cast<u32>(height),
  628. .depth = 1,
  629. },
  630. .selection = {
  631. .width = static_cast<u32>(width),
  632. .height = static_cast<u32>(height),
  633. .depth = 1,
  634. },
  635. };
  636. texture_2d->replace_sub_texture_data(level, input_layout, { xoffset, yoffset, 0 }, data);
  637. }
  638. void GLContext::sync_device_sampler_config()
  639. {
  640. if (!m_sampler_config_is_dirty)
  641. return;
  642. m_sampler_config_is_dirty = false;
  643. for (unsigned i = 0; i < m_texture_units.size(); ++i) {
  644. auto const& texture_unit = m_texture_units[i];
  645. if (!texture_unit.texture_2d_enabled())
  646. continue;
  647. GPU::SamplerConfig config;
  648. auto texture_2d = texture_unit.texture_2d_target_texture();
  649. VERIFY(!texture_2d.is_null());
  650. config.bound_image = texture_2d->device_image();
  651. config.level_of_detail_bias = texture_2d->level_of_detail_bias() + texture_unit.level_of_detail_bias();
  652. auto const& sampler = texture_2d->sampler();
  653. switch (sampler.min_filter()) {
  654. case GL_NEAREST:
  655. config.texture_min_filter = GPU::TextureFilter::Nearest;
  656. config.mipmap_filter = GPU::MipMapFilter::None;
  657. break;
  658. case GL_LINEAR:
  659. config.texture_min_filter = GPU::TextureFilter::Linear;
  660. config.mipmap_filter = GPU::MipMapFilter::None;
  661. break;
  662. case GL_NEAREST_MIPMAP_NEAREST:
  663. config.texture_min_filter = GPU::TextureFilter::Nearest;
  664. config.mipmap_filter = GPU::MipMapFilter::Nearest;
  665. break;
  666. case GL_LINEAR_MIPMAP_NEAREST:
  667. config.texture_min_filter = GPU::TextureFilter::Linear;
  668. config.mipmap_filter = GPU::MipMapFilter::Nearest;
  669. break;
  670. case GL_NEAREST_MIPMAP_LINEAR:
  671. config.texture_min_filter = GPU::TextureFilter::Nearest;
  672. config.mipmap_filter = GPU::MipMapFilter::Linear;
  673. break;
  674. case GL_LINEAR_MIPMAP_LINEAR:
  675. config.texture_min_filter = GPU::TextureFilter::Linear;
  676. config.mipmap_filter = GPU::MipMapFilter::Linear;
  677. break;
  678. default:
  679. VERIFY_NOT_REACHED();
  680. }
  681. switch (sampler.mag_filter()) {
  682. case GL_NEAREST:
  683. config.texture_mag_filter = GPU::TextureFilter::Nearest;
  684. break;
  685. case GL_LINEAR:
  686. config.texture_mag_filter = GPU::TextureFilter::Linear;
  687. break;
  688. default:
  689. VERIFY_NOT_REACHED();
  690. }
  691. switch (sampler.wrap_s_mode()) {
  692. case GL_CLAMP:
  693. config.texture_wrap_u = GPU::TextureWrapMode::Clamp;
  694. break;
  695. case GL_CLAMP_TO_BORDER:
  696. config.texture_wrap_u = GPU::TextureWrapMode::ClampToBorder;
  697. break;
  698. case GL_CLAMP_TO_EDGE:
  699. config.texture_wrap_u = GPU::TextureWrapMode::ClampToEdge;
  700. break;
  701. case GL_REPEAT:
  702. config.texture_wrap_u = GPU::TextureWrapMode::Repeat;
  703. break;
  704. case GL_MIRRORED_REPEAT:
  705. config.texture_wrap_u = GPU::TextureWrapMode::MirroredRepeat;
  706. break;
  707. default:
  708. VERIFY_NOT_REACHED();
  709. }
  710. switch (sampler.wrap_t_mode()) {
  711. case GL_CLAMP:
  712. config.texture_wrap_v = GPU::TextureWrapMode::Clamp;
  713. break;
  714. case GL_CLAMP_TO_BORDER:
  715. config.texture_wrap_v = GPU::TextureWrapMode::ClampToBorder;
  716. break;
  717. case GL_CLAMP_TO_EDGE:
  718. config.texture_wrap_v = GPU::TextureWrapMode::ClampToEdge;
  719. break;
  720. case GL_REPEAT:
  721. config.texture_wrap_v = GPU::TextureWrapMode::Repeat;
  722. break;
  723. case GL_MIRRORED_REPEAT:
  724. config.texture_wrap_v = GPU::TextureWrapMode::MirroredRepeat;
  725. break;
  726. default:
  727. VERIFY_NOT_REACHED();
  728. }
  729. auto& fixed_function_env = config.fixed_function_texture_environment;
  730. fixed_function_env.color = texture_unit.color();
  731. auto get_env_mode = [](GLenum mode) {
  732. switch (mode) {
  733. case GL_ADD:
  734. return GPU::TextureEnvMode::Add;
  735. case GL_BLEND:
  736. return GPU::TextureEnvMode::Blend;
  737. case GL_COMBINE:
  738. return GPU::TextureEnvMode::Combine;
  739. case GL_DECAL:
  740. return GPU::TextureEnvMode::Decal;
  741. case GL_MODULATE:
  742. return GPU::TextureEnvMode::Modulate;
  743. case GL_REPLACE:
  744. return GPU::TextureEnvMode::Replace;
  745. default:
  746. VERIFY_NOT_REACHED();
  747. }
  748. };
  749. fixed_function_env.env_mode = get_env_mode(texture_unit.env_mode());
  750. fixed_function_env.alpha_scale = texture_unit.alpha_scale();
  751. fixed_function_env.rgb_scale = texture_unit.rgb_scale();
  752. auto get_combinator = [](GLenum combinator) {
  753. switch (combinator) {
  754. case GL_ADD:
  755. return GPU::TextureCombinator::Add;
  756. case GL_ADD_SIGNED:
  757. return GPU::TextureCombinator::AddSigned;
  758. case GL_DOT3_RGB:
  759. return GPU::TextureCombinator::Dot3RGB;
  760. case GL_DOT3_RGBA:
  761. return GPU::TextureCombinator::Dot3RGBA;
  762. case GL_INTERPOLATE:
  763. return GPU::TextureCombinator::Interpolate;
  764. case GL_MODULATE:
  765. return GPU::TextureCombinator::Modulate;
  766. case GL_REPLACE:
  767. return GPU::TextureCombinator::Replace;
  768. case GL_SUBTRACT:
  769. return GPU::TextureCombinator::Subtract;
  770. default:
  771. VERIFY_NOT_REACHED();
  772. }
  773. };
  774. fixed_function_env.alpha_combinator = get_combinator(texture_unit.alpha_combinator());
  775. fixed_function_env.rgb_combinator = get_combinator(texture_unit.rgb_combinator());
  776. auto get_operand = [](GLenum operand) {
  777. switch (operand) {
  778. case GL_ONE_MINUS_SRC_ALPHA:
  779. return GPU::TextureOperand::OneMinusSourceAlpha;
  780. case GL_ONE_MINUS_SRC_COLOR:
  781. return GPU::TextureOperand::OneMinusSourceColor;
  782. case GL_SRC_ALPHA:
  783. return GPU::TextureOperand::SourceAlpha;
  784. case GL_SRC_COLOR:
  785. return GPU::TextureOperand::SourceColor;
  786. default:
  787. VERIFY_NOT_REACHED();
  788. }
  789. };
  790. auto get_source = [](GLenum source) {
  791. switch (source) {
  792. case GL_CONSTANT:
  793. return GPU::TextureSource::Constant;
  794. case GL_PREVIOUS:
  795. return GPU::TextureSource::Previous;
  796. case GL_PRIMARY_COLOR:
  797. return GPU::TextureSource::PrimaryColor;
  798. case GL_TEXTURE:
  799. return GPU::TextureSource::Texture;
  800. case GL_TEXTURE0 ... GL_TEXTURE31:
  801. return GPU::TextureSource::TextureStage;
  802. default:
  803. VERIFY_NOT_REACHED();
  804. }
  805. };
  806. for (size_t j = 0; j < 3; ++j) {
  807. fixed_function_env.alpha_operand[j] = get_operand(texture_unit.alpha_operand(j));
  808. fixed_function_env.alpha_source[j] = get_source(texture_unit.alpha_source(j));
  809. if (fixed_function_env.alpha_source[j] == GPU::TextureSource::TextureStage)
  810. fixed_function_env.alpha_source_texture_stage = texture_unit.alpha_source(j) - GL_TEXTURE0;
  811. fixed_function_env.rgb_operand[j] = get_operand(texture_unit.rgb_operand(j));
  812. fixed_function_env.rgb_source[j] = get_source(texture_unit.rgb_source(j));
  813. if (fixed_function_env.rgb_source[j] == GPU::TextureSource::TextureStage)
  814. fixed_function_env.rgb_source_texture_stage = texture_unit.rgb_source(j) - GL_TEXTURE0;
  815. }
  816. config.border_color = sampler.border_color();
  817. m_rasterizer->set_sampler_config(i, config);
  818. }
  819. }
  820. void GLContext::sync_device_texture_units()
  821. {
  822. if (!m_texture_units_dirty)
  823. return;
  824. m_texture_units_dirty = false;
  825. for (GPU::TextureUnitIndex i = 0; i < m_device_info.num_texture_units; ++i) {
  826. GPU::TextureUnitConfiguration texture_unit_configuration;
  827. texture_unit_configuration.enabled = m_texture_units[i].texture_2d_enabled();
  828. texture_unit_configuration.transformation_matrix = m_texture_units[i].texture_matrix();
  829. // Tex coord generation
  830. u8 enabled_coordinates = GPU::TexCoordGenerationCoordinate::None;
  831. for (GLenum capability = GL_TEXTURE_GEN_S; capability <= GL_TEXTURE_GEN_Q; ++capability) {
  832. auto const context_coordinate_config = texture_coordinate_generation(i, capability);
  833. if (!context_coordinate_config.enabled)
  834. continue;
  835. GPU::TexCoordGeneration* texcoord_generation;
  836. switch (capability) {
  837. case GL_TEXTURE_GEN_S:
  838. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::S;
  839. texcoord_generation = &texture_unit_configuration.tex_coord_generation[0];
  840. break;
  841. case GL_TEXTURE_GEN_T:
  842. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::T;
  843. texcoord_generation = &texture_unit_configuration.tex_coord_generation[1];
  844. break;
  845. case GL_TEXTURE_GEN_R:
  846. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::R;
  847. texcoord_generation = &texture_unit_configuration.tex_coord_generation[2];
  848. break;
  849. case GL_TEXTURE_GEN_Q:
  850. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::Q;
  851. texcoord_generation = &texture_unit_configuration.tex_coord_generation[3];
  852. break;
  853. default:
  854. VERIFY_NOT_REACHED();
  855. }
  856. switch (context_coordinate_config.generation_mode) {
  857. case GL_OBJECT_LINEAR:
  858. texcoord_generation->mode = GPU::TexCoordGenerationMode::ObjectLinear;
  859. texcoord_generation->coefficients = context_coordinate_config.object_plane_coefficients;
  860. break;
  861. case GL_EYE_LINEAR:
  862. texcoord_generation->mode = GPU::TexCoordGenerationMode::EyeLinear;
  863. texcoord_generation->coefficients = context_coordinate_config.eye_plane_coefficients;
  864. break;
  865. case GL_SPHERE_MAP:
  866. texcoord_generation->mode = GPU::TexCoordGenerationMode::SphereMap;
  867. break;
  868. case GL_REFLECTION_MAP:
  869. texcoord_generation->mode = GPU::TexCoordGenerationMode::ReflectionMap;
  870. break;
  871. case GL_NORMAL_MAP:
  872. texcoord_generation->mode = GPU::TexCoordGenerationMode::NormalMap;
  873. break;
  874. default:
  875. VERIFY_NOT_REACHED();
  876. }
  877. }
  878. texture_unit_configuration.tex_coord_generation_enabled = enabled_coordinates;
  879. m_rasterizer->set_texture_unit_configuration(i, texture_unit_configuration);
  880. }
  881. }
  882. }