Texture.cpp 40 KB

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