blob: 2bf3b892726892830067656df829065ed3e84ca1 [file] [log] [blame]
Geoff Lang6a1e6b92014-11-06 10:42:45 -05001//
2// Copyright 2014 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
Geoff Langda88add2014-12-01 10:22:01 -05007// FramebufferD3D.cpp: Implements the DefaultAttachmentD3D and FramebufferD3D classes.
Geoff Lang6a1e6b92014-11-06 10:42:45 -05008
Geoff Lang2b5420c2014-11-19 14:20:15 -05009#include "libANGLE/renderer/d3d/FramebufferD3D.h"
Jamie Madillc46f45d2015-03-31 13:20:55 -040010
Jamie Madill60ec6ea2016-01-22 15:27:19 -050011#include "common/BitSetIterator.h"
Geoff Langbce529e2014-12-01 12:48:41 -050012#include "libANGLE/formatutils.h"
Geoff Lang54bd5a42014-12-01 12:51:04 -050013#include "libANGLE/Framebuffer.h"
Geoff Langb5d8f232014-12-04 15:43:01 -050014#include "libANGLE/FramebufferAttachment.h"
Jamie Madillc46f45d2015-03-31 13:20:55 -040015#include "libANGLE/Surface.h"
Jamie Madill8415b5f2016-04-26 13:41:39 -040016#include "libANGLE/renderer/ContextImpl.h"
Jamie Madillc46f45d2015-03-31 13:20:55 -040017#include "libANGLE/renderer/d3d/RendererD3D.h"
18#include "libANGLE/renderer/d3d/RenderbufferD3D.h"
19#include "libANGLE/renderer/d3d/RenderTargetD3D.h"
20#include "libANGLE/renderer/d3d/SurfaceD3D.h"
21#include "libANGLE/renderer/d3d/SwapChainD3D.h"
22#include "libANGLE/renderer/d3d/TextureD3D.h"
Geoff Lang6a1e6b92014-11-06 10:42:45 -050023
24namespace rx
25{
26
Jamie Madillf75ab352015-03-16 10:46:52 -040027namespace
28{
29
30ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask)
31{
32 ClearParameters clearParams;
33 memset(&clearParams, 0, sizeof(ClearParameters));
34
35 const auto &blendState = state.getBlendState();
36
37 for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
38 {
39 clearParams.clearColor[i] = false;
40 }
41 clearParams.colorFClearValue = state.getColorClearValue();
42 clearParams.colorClearType = GL_FLOAT;
43 clearParams.colorMaskRed = blendState.colorMaskRed;
44 clearParams.colorMaskGreen = blendState.colorMaskGreen;
45 clearParams.colorMaskBlue = blendState.colorMaskBlue;
46 clearParams.colorMaskAlpha = blendState.colorMaskAlpha;
47 clearParams.clearDepth = false;
48 clearParams.depthClearValue = state.getDepthClearValue();
49 clearParams.clearStencil = false;
50 clearParams.stencilClearValue = state.getStencilClearValue();
51 clearParams.stencilWriteMask = state.getDepthStencilState().stencilWritemask;
52 clearParams.scissorEnabled = state.isScissorTestEnabled();
53 clearParams.scissor = state.getScissor();
54
55 const gl::Framebuffer *framebufferObject = state.getDrawFramebuffer();
56 if (mask & GL_COLOR_BUFFER_BIT)
57 {
Geoff Langa15472a2015-08-11 11:48:03 -040058 if (framebufferObject->hasEnabledDrawBuffer())
Jamie Madillf75ab352015-03-16 10:46:52 -040059 {
60 for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
61 {
62 clearParams.clearColor[i] = true;
63 }
64 }
65 }
66
67 if (mask & GL_DEPTH_BUFFER_BIT)
68 {
69 if (state.getDepthStencilState().depthMask && framebufferObject->getDepthbuffer() != NULL)
70 {
71 clearParams.clearDepth = true;
72 }
73 }
74
75 if (mask & GL_STENCIL_BUFFER_BIT)
76 {
77 if (framebufferObject->getStencilbuffer() != NULL &&
78 framebufferObject->getStencilbuffer()->getStencilSize() > 0)
79 {
80 clearParams.clearStencil = true;
81 }
82 }
83
84 return clearParams;
85}
86
87}
88
Jamie Madill48ef11b2016-04-27 15:21:52 -040089FramebufferD3D::FramebufferD3D(const gl::FramebufferState &data, RendererD3D *renderer)
Jamie Madill60ec6ea2016-01-22 15:27:19 -050090 : FramebufferImpl(data), mRenderer(renderer)
Geoff Langda88add2014-12-01 10:22:01 -050091{
Geoff Langda88add2014-12-01 10:22:01 -050092}
93
94FramebufferD3D::~FramebufferD3D()
95{
96}
97
Jamie Madill8415b5f2016-04-26 13:41:39 -040098gl::Error FramebufferD3D::clear(ContextImpl *context, GLbitfield mask)
Geoff Langb04dc822014-12-01 12:02:02 -050099{
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700100 ClearParameters clearParams = GetClearParameters(context->getGLState(), mask);
Jamie Madill8415b5f2016-04-26 13:41:39 -0400101 return clearImpl(context, clearParams);
Geoff Langb04dc822014-12-01 12:02:02 -0500102}
103
Jamie Madill8415b5f2016-04-26 13:41:39 -0400104gl::Error FramebufferD3D::clearBufferfv(ContextImpl *context,
Dian Xiang40281592015-11-19 18:24:44 -0800105 GLenum buffer,
106 GLint drawbuffer,
107 const GLfloat *values)
Geoff Langb04dc822014-12-01 12:02:02 -0500108{
109 // glClearBufferfv can be called to clear the color buffer or depth buffer
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700110 ClearParameters clearParams = GetClearParameters(context->getGLState(), 0);
Geoff Langb04dc822014-12-01 12:02:02 -0500111
112 if (buffer == GL_COLOR)
113 {
114 for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
115 {
116 clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
117 }
118 clearParams.colorFClearValue = gl::ColorF(values[0], values[1], values[2], values[3]);
119 clearParams.colorClearType = GL_FLOAT;
120 }
121
122 if (buffer == GL_DEPTH)
123 {
124 clearParams.clearDepth = true;
125 clearParams.depthClearValue = values[0];
126 }
127
Jamie Madill8415b5f2016-04-26 13:41:39 -0400128 return clearImpl(context, clearParams);
Geoff Langb04dc822014-12-01 12:02:02 -0500129}
130
Jamie Madill8415b5f2016-04-26 13:41:39 -0400131gl::Error FramebufferD3D::clearBufferuiv(ContextImpl *context,
Dian Xiang40281592015-11-19 18:24:44 -0800132 GLenum buffer,
133 GLint drawbuffer,
134 const GLuint *values)
Geoff Langb04dc822014-12-01 12:02:02 -0500135{
136 // glClearBufferuiv can only be called to clear a color buffer
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700137 ClearParameters clearParams = GetClearParameters(context->getGLState(), 0);
Geoff Langb04dc822014-12-01 12:02:02 -0500138 for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
139 {
140 clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
141 }
142 clearParams.colorUIClearValue = gl::ColorUI(values[0], values[1], values[2], values[3]);
143 clearParams.colorClearType = GL_UNSIGNED_INT;
144
Jamie Madill8415b5f2016-04-26 13:41:39 -0400145 return clearImpl(context, clearParams);
Geoff Langb04dc822014-12-01 12:02:02 -0500146}
147
Jamie Madill8415b5f2016-04-26 13:41:39 -0400148gl::Error FramebufferD3D::clearBufferiv(ContextImpl *context,
Dian Xiang40281592015-11-19 18:24:44 -0800149 GLenum buffer,
150 GLint drawbuffer,
151 const GLint *values)
Geoff Langb04dc822014-12-01 12:02:02 -0500152{
153 // glClearBufferiv can be called to clear the color buffer or stencil buffer
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700154 ClearParameters clearParams = GetClearParameters(context->getGLState(), 0);
Geoff Langb04dc822014-12-01 12:02:02 -0500155
156 if (buffer == GL_COLOR)
157 {
158 for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
159 {
160 clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
161 }
162 clearParams.colorIClearValue = gl::ColorI(values[0], values[1], values[2], values[3]);
163 clearParams.colorClearType = GL_INT;
164 }
165
166 if (buffer == GL_STENCIL)
167 {
168 clearParams.clearStencil = true;
169 clearParams.stencilClearValue = values[1];
170 }
171
Jamie Madill8415b5f2016-04-26 13:41:39 -0400172 return clearImpl(context, clearParams);
Geoff Langb04dc822014-12-01 12:02:02 -0500173}
174
Jamie Madill8415b5f2016-04-26 13:41:39 -0400175gl::Error FramebufferD3D::clearBufferfi(ContextImpl *context,
Dian Xiang40281592015-11-19 18:24:44 -0800176 GLenum buffer,
177 GLint drawbuffer,
178 GLfloat depth,
179 GLint stencil)
Geoff Langb04dc822014-12-01 12:02:02 -0500180{
181 // glClearBufferfi can only be called to clear a depth stencil buffer
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700182 ClearParameters clearParams = GetClearParameters(context->getGLState(), 0);
Geoff Langb04dc822014-12-01 12:02:02 -0500183 clearParams.clearDepth = true;
184 clearParams.depthClearValue = depth;
185 clearParams.clearStencil = true;
186 clearParams.stencilClearValue = stencil;
187
Jamie Madill8415b5f2016-04-26 13:41:39 -0400188 return clearImpl(context, clearParams);
Geoff Langb04dc822014-12-01 12:02:02 -0500189}
190
Geoff Langbce529e2014-12-01 12:48:41 -0500191GLenum FramebufferD3D::getImplementationColorReadFormat() const
192{
Jamie Madill48ef11b2016-04-27 15:21:52 -0400193 const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment();
Geoff Langbce529e2014-12-01 12:48:41 -0500194
Jamie Madillb885e572015-02-03 16:16:04 -0500195 if (readAttachment == nullptr)
Geoff Langbce529e2014-12-01 12:48:41 -0500196 {
197 return GL_NONE;
198 }
199
Geoff Langc2e75af2015-01-05 14:26:24 -0500200 RenderTargetD3D *attachmentRenderTarget = NULL;
Jamie Madill8cf813c2015-05-04 12:55:18 -0400201 gl::Error error = readAttachment->getRenderTarget(&attachmentRenderTarget);
Geoff Langd8a22582014-12-17 15:28:23 -0500202 if (error.isError())
203 {
204 return GL_NONE;
205 }
Geoff Langbce529e2014-12-01 12:48:41 -0500206
Geoff Langd8a22582014-12-17 15:28:23 -0500207 GLenum implementationFormat = getRenderTargetImplementationFormat(attachmentRenderTarget);
208 const gl::InternalFormat &implementationFormatInfo = gl::GetInternalFormatInfo(implementationFormat);
209
210 return implementationFormatInfo.format;
Geoff Langbce529e2014-12-01 12:48:41 -0500211}
212
213GLenum FramebufferD3D::getImplementationColorReadType() const
214{
Jamie Madill48ef11b2016-04-27 15:21:52 -0400215 const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment();
Geoff Langbce529e2014-12-01 12:48:41 -0500216
Jamie Madillb885e572015-02-03 16:16:04 -0500217 if (readAttachment == nullptr)
Geoff Langbce529e2014-12-01 12:48:41 -0500218 {
219 return GL_NONE;
220 }
221
Geoff Langc2e75af2015-01-05 14:26:24 -0500222 RenderTargetD3D *attachmentRenderTarget = NULL;
Jamie Madill8cf813c2015-05-04 12:55:18 -0400223 gl::Error error = readAttachment->getRenderTarget(&attachmentRenderTarget);
Geoff Langd8a22582014-12-17 15:28:23 -0500224 if (error.isError())
225 {
226 return GL_NONE;
227 }
Geoff Langbce529e2014-12-01 12:48:41 -0500228
Geoff Langd8a22582014-12-17 15:28:23 -0500229 GLenum implementationFormat = getRenderTargetImplementationFormat(attachmentRenderTarget);
230 const gl::InternalFormat &implementationFormatInfo = gl::GetInternalFormatInfo(implementationFormat);
231
232 return implementationFormatInfo.type;
Geoff Langbce529e2014-12-01 12:48:41 -0500233}
234
Jamie Madill8415b5f2016-04-26 13:41:39 -0400235gl::Error FramebufferD3D::readPixels(ContextImpl *context,
236 const gl::Rectangle &area,
237 GLenum format,
238 GLenum type,
239 GLvoid *pixels) const
Geoff Langbce529e2014-12-01 12:48:41 -0500240{
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700241 const gl::PixelPackState &packState = context->getGLState().getPackState();
Jamie Madill87de3622015-03-16 10:41:44 -0400242
Geoff Langbce529e2014-12-01 12:48:41 -0500243 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(format, type);
244 const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(sizedInternalFormat);
Jamie Madille2e406c2016-06-02 13:04:10 -0400245 GLuint outputPitch = 0;
246 ANGLE_TRY_RESULT(
247 sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment, packState.rowLength),
248 outputPitch);
Olli Etuaho989cac32016-06-08 16:18:49 -0700249 GLuint outputSkipBytes = 0;
250 ANGLE_TRY_RESULT(sizedFormatInfo.computeSkipBytes(outputPitch, 0, 0, packState.skipRows,
251 packState.skipPixels, false),
252 outputSkipBytes);
Geoff Langbce529e2014-12-01 12:48:41 -0500253
Minmin Gongadff67b2015-10-14 10:34:45 -0400254 return readPixelsImpl(area, format, type, outputPitch, packState,
255 reinterpret_cast<uint8_t *>(pixels) + outputSkipBytes);
Geoff Langbce529e2014-12-01 12:48:41 -0500256}
257
Jamie Madill8415b5f2016-04-26 13:41:39 -0400258gl::Error FramebufferD3D::blit(ContextImpl *context,
259 const gl::Rectangle &sourceArea,
260 const gl::Rectangle &destArea,
261 GLbitfield mask,
262 GLenum filter)
Geoff Lang54bd5a42014-12-01 12:51:04 -0500263{
Jamie Madilldfde6ab2016-06-09 07:07:18 -0700264 const auto &glState = context->getGLState();
Jamie Madill8415b5f2016-04-26 13:41:39 -0400265 const gl::Framebuffer *sourceFramebuffer = glState.getReadFramebuffer();
Geoff Lang54bd5a42014-12-01 12:51:04 -0500266 bool blitRenderTarget = false;
Jamie Madill48ef11b2016-04-27 15:21:52 -0400267 if ((mask & GL_COLOR_BUFFER_BIT) && sourceFramebuffer->getReadColorbuffer() != nullptr &&
268 mState.getFirstColorAttachment() != nullptr)
Geoff Lang54bd5a42014-12-01 12:51:04 -0500269 {
270 blitRenderTarget = true;
271 }
272
273 bool blitStencil = false;
Jamie Madill48ef11b2016-04-27 15:21:52 -0400274 if ((mask & GL_STENCIL_BUFFER_BIT) && sourceFramebuffer->getStencilbuffer() != nullptr &&
275 mState.getStencilAttachment() != nullptr)
Geoff Lang54bd5a42014-12-01 12:51:04 -0500276 {
277 blitStencil = true;
278 }
279
280 bool blitDepth = false;
Jamie Madill48ef11b2016-04-27 15:21:52 -0400281 if ((mask & GL_DEPTH_BUFFER_BIT) && sourceFramebuffer->getDepthbuffer() != nullptr &&
282 mState.getDepthAttachment() != nullptr)
Geoff Lang54bd5a42014-12-01 12:51:04 -0500283 {
284 blitDepth = true;
285 }
286
287 if (blitRenderTarget || blitDepth || blitStencil)
288 {
Jamie Madill8415b5f2016-04-26 13:41:39 -0400289 const gl::Rectangle *scissor =
290 glState.isScissorTestEnabled() ? &glState.getScissor() : nullptr;
291 gl::Error error = blitImpl(sourceArea, destArea, scissor, blitRenderTarget, blitDepth,
292 blitStencil, filter, sourceFramebuffer);
Geoff Lang54bd5a42014-12-01 12:51:04 -0500293 if (error.isError())
294 {
295 return error;
296 }
297 }
298
299 return gl::Error(GL_NO_ERROR);
300}
301
Jamie Madillcc86d642015-11-24 13:00:07 -0500302bool FramebufferD3D::checkStatus() const
Geoff Lang748f74e2014-12-01 11:25:34 -0500303{
Geoff Langc2520562015-04-29 11:42:33 -0400304 // if we have both a depth and stencil buffer, they must refer to the same object
305 // since we only support packed_depth_stencil and not separate depth and stencil
Jamie Madill48ef11b2016-04-27 15:21:52 -0400306 if (mState.getDepthAttachment() != nullptr && mState.getStencilAttachment() != nullptr &&
307 mState.getDepthStencilAttachment() == nullptr)
Geoff Langc2520562015-04-29 11:42:33 -0400308 {
Jamie Madillcc86d642015-11-24 13:00:07 -0500309 return false;
Geoff Langc2520562015-04-29 11:42:33 -0400310 }
311
Geoff Lang4b7f12b2016-06-21 16:47:07 -0400312 // D3D11 does not allow for overlapping RenderTargetViews, so ensure uniqueness by checking the
313 // enabled draw buffers
314 for (size_t firstDrawBufferIdx = 0; firstDrawBufferIdx < mState.getDrawBufferCount();
315 firstDrawBufferIdx++)
Geoff Lang748f74e2014-12-01 11:25:34 -0500316 {
Geoff Lang4b7f12b2016-06-21 16:47:07 -0400317 const gl::FramebufferAttachment *firstAttachment = mState.getDrawBuffer(firstDrawBufferIdx);
318 if (firstAttachment == nullptr)
Geoff Lang748f74e2014-12-01 11:25:34 -0500319 {
Geoff Lang4b7f12b2016-06-21 16:47:07 -0400320 continue;
321 }
322
323 for (size_t secondDrawBufferIdx = firstDrawBufferIdx + 1;
324 secondDrawBufferIdx < mState.getDrawBufferCount(); secondDrawBufferIdx++)
325 {
326 const gl::FramebufferAttachment *secondAttachment =
327 mState.getDrawBuffer(secondDrawBufferIdx);
328 if (secondAttachment == nullptr)
Geoff Lang748f74e2014-12-01 11:25:34 -0500329 {
Geoff Lang4b7f12b2016-06-21 16:47:07 -0400330 continue;
331 }
332
333 if (firstAttachment->id() == secondAttachment->id() &&
334 firstAttachment->type() == secondAttachment->type())
335 {
336 return false;
Geoff Lang748f74e2014-12-01 11:25:34 -0500337 }
338 }
339 }
340
Jamie Madillcc86d642015-11-24 13:00:07 -0500341 // D3D requires all render targets to have the same dimensions.
Jamie Madill48ef11b2016-04-27 15:21:52 -0400342 if (!mState.attachmentsHaveSameDimensions())
Jamie Madillcc86d642015-11-24 13:00:07 -0500343 {
344 return false;
345 }
346
347 return true;
Geoff Lang748f74e2014-12-01 11:25:34 -0500348}
349
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500350void FramebufferD3D::syncState(const gl::Framebuffer::DirtyBits &dirtyBits)
Jamie Madill7147f012015-03-05 15:41:40 -0500351{
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500352 bool invalidateColorAttachmentCache = false;
353
354 if (!mColorAttachmentsForRender.valid())
Jamie Madill85a18042015-03-05 15:41:41 -0500355 {
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500356 invalidateColorAttachmentCache = true;
357 }
358
359 for (auto dirtyBit : angle::IterateBitSet(dirtyBits))
360 {
361 if ((dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 &&
362 dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) ||
363 dirtyBit == gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS)
364 {
365 invalidateColorAttachmentCache = true;
366 }
367 }
368
369 if (!invalidateColorAttachmentCache)
370 {
371 return;
Jamie Madill85a18042015-03-05 15:41:41 -0500372 }
373
Jamie Madill7147f012015-03-05 15:41:40 -0500374 // Does not actually free memory
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500375 gl::AttachmentList colorAttachmentsForRender;
Jamie Madill7147f012015-03-05 15:41:40 -0500376
Jamie Madill48ef11b2016-04-27 15:21:52 -0400377 const auto &colorAttachments = mState.getColorAttachments();
378 const auto &drawBufferStates = mState.getDrawBufferStates();
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500379 const auto &workarounds = mRenderer->getWorkarounds();
Jamie Madill2d06b732015-04-20 12:53:28 -0400380
Jamie Madillb6bda4a2015-04-20 12:53:26 -0400381 for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex)
Jamie Madill7147f012015-03-05 15:41:40 -0500382 {
Jamie Madillb6bda4a2015-04-20 12:53:26 -0400383 GLenum drawBufferState = drawBufferStates[attachmentIndex];
Jamie Madill2d06b732015-04-20 12:53:28 -0400384 const gl::FramebufferAttachment &colorAttachment = colorAttachments[attachmentIndex];
Jamie Madill7147f012015-03-05 15:41:40 -0500385
Jamie Madill2d06b732015-04-20 12:53:28 -0400386 if (colorAttachment.isAttached() && drawBufferState != GL_NONE)
Jamie Madill7147f012015-03-05 15:41:40 -0500387 {
388 ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex));
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500389 colorAttachmentsForRender.push_back(&colorAttachment);
Jamie Madill7147f012015-03-05 15:41:40 -0500390 }
Jamie Madillb6bda4a2015-04-20 12:53:26 -0400391 else if (!workarounds.mrtPerfWorkaround)
392 {
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500393 colorAttachmentsForRender.push_back(nullptr);
Jamie Madillb6bda4a2015-04-20 12:53:26 -0400394 }
Jamie Madill7147f012015-03-05 15:41:40 -0500395 }
396
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500397 mColorAttachmentsForRender = std::move(colorAttachmentsForRender);
Jamie Madill7147f012015-03-05 15:41:40 -0500398}
399
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500400const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender() const
401{
402 ASSERT(mColorAttachmentsForRender.valid());
403 return mColorAttachmentsForRender.value();
Geoff Lang6a1e6b92014-11-06 10:42:45 -0500404}
Jamie Madill60ec6ea2016-01-22 15:27:19 -0500405
406} // namespace rx