Texture.cpp 39 KB

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