Index: renderer/backend/stages/SurfacePassesStage.cpp
===================================================================
--- renderer/backend/stages/SurfacePassesStage.cpp	(revision 10623)
+++ renderer/backend/stages/SurfacePassesStage.cpp	(working copy)
@@ -334,7 +334,7 @@
 
 	// bind texture (either 2D texture or cubemap is used)
 	GL_SelectTexture( 0 );
-	BindVariableStageImage( &pStage->texture, regs );
+	BindVariableStageImage( drawSurf, &pStage->texture, regs );
 	if ( texgen == TEXGEN_CUBEMAP ) {
 		uniforms->cubemap.Set( 0 );
 		uniforms->texture.Set( 1 );
@@ -549,7 +549,7 @@
 	RB_DrawElementsWithCounters( drawSurf, DCK_SURFACE_PASS );
 }
 
-void SurfacePassesStage::BindVariableStageImage( const textureStage_t *texture, const float *regs ) {
+void SurfacePassesStage::BindVariableStageImage( const drawSurf_t *drawSurf, const textureStage_t *texture, const float *regs ) {
 	if ( texture->cinematic ) {
 		if ( r_skipDynamicTextures.GetBool() ) {
 			globalImages->defaultImage->Bind();
@@ -568,5 +568,8 @@
 		}
 	} else if ( texture->image ) {
 		texture->image->Bind();
+	} else if ( texture->dynamic && drawSurf->dynamicImageOverride ) {
+		// stgatilov #6434: image generated by subview
+		drawSurf->dynamicImageOverride->Bind();
 	}
 }
Index: renderer/backend/stages/SurfacePassesStage.h
===================================================================
--- renderer/backend/stages/SurfacePassesStage.h	(revision 10623)
+++ renderer/backend/stages/SurfacePassesStage.h	(working copy)
@@ -54,5 +54,5 @@
 	void DrawSoftParticle( const drawSurf_t *drawSurf, const shaderStage_t *pStage );
 	void DrawCustomShader( const drawSurf_t *drawSurf, const shaderStage_t *pStage );
 
-	void BindVariableStageImage( const textureStage_t *texture, const float *regs );
+	void BindVariableStageImage( const drawSurf_t *drawSurf, const textureStage_t *texture, const float *regs );
 };
Index: renderer/frontend/tr_light.cpp
===================================================================
--- renderer/frontend/tr_light.cpp	(revision 10623)
+++ renderer/frontend/tr_light.cpp	(working copy)
@@ -1269,6 +1269,7 @@
 	drawSurf->CopyGeo( tri );
 	drawSurf->space = space;
 	drawSurf->material = material;
+	drawSurf->dynamicImageOverride = nullptr;
 	drawSurf->scissorRect = scissor;
 	drawSurf->sort = material->GetSort();
 	drawSurf->dsFlags = 0;
Index: renderer/frontend/tr_subview.cpp
===================================================================
--- renderer/frontend/tr_subview.cpp	(revision 10623)
+++ renderer/frontend/tr_subview.cpp	(working copy)
@@ -19,12 +19,14 @@
 #include "renderer/tr_local.h"
 #include "game/Grabber.h"
 
+
+static const int MAX_SUBVIEW_DEPTH = 4;
+
 typedef struct {
 	idVec3		origin;
 	idMat3		axis;
 } orientation_t;
 
-
 /*
 =================
 R_MirrorPoint
@@ -274,9 +276,9 @@
 */
 static void R_RemoteRender( drawSurf_t *surf, textureStage_t *stage ) {
 
-	// remote views can be reused in a single frame
+/*	// remote views can be reused in a single frame
 	if ( stage->dynamicFrameCount == tr.frameCount ) 
-		return;
+		return;*/
 
 	// if the entity doesn't have a remoteRenderView, do nothing
 	if ( !surf->space->entityDef->parms.remoteRenderView ) 
@@ -288,6 +290,8 @@
 
 	parms->isSubview = true;
 	parms->isMirror = false;
+	// if we see remote screen in mirror, drop mirror's clip plane
+	parms->clipPlane = nullptr;
 
 	parms->renderView = *surf->space->entityDef->parms.remoteRenderView;
 	parms->renderView.viewID = VID_SUBVIEW;	// clear to allow player bodies to show up, and suppress view weapons
@@ -314,11 +318,17 @@
 	R_RenderView(*parms);
 
 	// copy this rendering to the image
-	stage->dynamicFrameCount = tr.frameCount;
+	/*stage->dynamicFrameCount = tr.frameCount;
 	if ( !stage->image )
 		stage->image = globalImages->scratchImage;
 
-	tr.CaptureRenderToImage( *stage->image->AsScratch() );
+	tr.CaptureRenderToImage( *stage->image->AsScratch() );*/
+
+	stage->image = nullptr;
+	idImageScratch *outputTexture = tr.CreateImageForSubview();
+	tr.CaptureRenderToImage( *outputTexture );
+	surf->dynamicImageOverride = outputTexture;
+
 	tr.UnCrop();
 }
 
@@ -330,13 +340,13 @@
 void R_MirrorRender( drawSurf_t *surf, textureStage_t *stage, idScreenRect& scissor ) {
 	viewDef_t		*parms;
 
-	if ( tr.viewDef->superView && tr.viewDef->superView->isSubview ) // #4615 HOM effect - only draw mirrors from player's view and top-level subviews
+	/*if ( tr.viewDef->superView && tr.viewDef->superView->isSubview ) // #4615 HOM effect - only draw mirrors from player's view and top-level subviews
 		return;
 
 	// remote views can be reused in a single frame
 	if ( stage->dynamicFrameCount == tr.frameCount ) {
 		return;
-	}
+	}*/
 
 	// issue a new view command
 	parms = R_MirrorViewBySurface( surf );
@@ -365,11 +375,17 @@
 	R_RenderView( *parms );
 
 	// copy this rendering to the image
-	stage->dynamicFrameCount = tr.frameCount;
+	/*stage->dynamicFrameCount = tr.frameCount;
 	if ( !stage->image )
 		stage->image = globalImages->scratchImage;
 
-	tr.CaptureRenderToImage( *stage->image->AsScratch() );
+	tr.CaptureRenderToImage( *stage->image->AsScratch() );*/
+
+	stage->image = nullptr;
+	idImageScratch *outputTexture = tr.CreateImageForSubview();
+	tr.CaptureRenderToImage( *outputTexture );
+	surf->dynamicImageOverride = outputTexture;
+
 	//tr.UnCrop();
 }
 
@@ -678,7 +694,6 @@
 */
 bool	R_GenerateSurfaceSubview( drawSurf_t *drawSurf ) {
 	idBounds		ndcBounds;
-	viewDef_t		*parms;
 	const idMaterial		*shader;
 
 	// for testing the performance hit
@@ -690,7 +705,7 @@
 
 	shader = drawSurf->material;
 
-	// never recurse through a subview surface that we are
+	/*// never recurse through a subview surface that we are
 	// already seeing through
 	for ( parms = tr.viewDef ; parms ; parms = parms->superView ) {
 		if ( parms->subviewSurface
@@ -700,6 +715,12 @@
 		}
 	}
 	if ( parms ) 
+		return false;*/
+
+	int depth = 0;
+	for (viewDef_s *view = tr.viewDef; view; view = view->superView)
+		depth++;
+	if (depth > MAX_SUBVIEW_DEPTH)
 		return false;
 
 	// crop the scissor bounds based on the precise cull
@@ -787,10 +808,14 @@
 		if ( !shader || !shader->HasSubview() )
 			continue;
 
-		if ( shader->GetSort() != SS_PORTAL_SKY ) // portal sky needs to be the last one, and only once
+		if ( shader->GetSort() != SS_PORTAL_SKY ) {// portal sky needs to be the last one, and only once
 			if ( R_GenerateSurfaceSubview( drawSurf ) ) {
 				subviews = true;
+			} else {
+				// #6434: probably blocked due to limits: render as black image
+				drawSurf->dynamicImageOverride = globalImages->blackImage;
 			}
+		}
 	}
 
 	static bool dontReenter = false;
Index: renderer/RenderSystem.cpp
===================================================================
--- renderer/RenderSystem.cpp	(revision 10623)
+++ renderer/RenderSystem.cpp	(working copy)
@@ -702,6 +702,8 @@
 	// we can now release the vertexes used this frame
 	vertexCache.EndFrame();
 
+	FreeOldSubviewImages();
+
 	if ( session->writeDemo ) {
 		session->writeDemo->WriteInt( DS_RENDER );
 		session->writeDemo->WriteInt( DC_END_FRAME );
@@ -741,6 +743,70 @@
 	viewport.y2 = idMath::FtoiTrunc( ( rc.y + rc.height ) - floor( renderView.y * hRatio + 0.5f ) - 1 );
 }
 
+
+void idRenderSystemLocal::FreeOldSubviewImages() {
+	// this method should be called outside parallel section
+	assert(!session->IsFrontend());
+
+	static const int SUBVIEW_IMAGE_KILL_AFTER_FRAMES = 100;
+
+	for (int i = 0; i < subviewImages.Num(); i++) {
+		ImageForSubview &si = subviewImages[i];
+		if (si.purged)
+			continue;
+
+		if (frameCount - si.lastUsedFrameCount > SUBVIEW_IMAGE_KILL_AFTER_FRAMES) {
+			si.purged = true;
+			si.image->PurgeImage();
+		}
+	}
+}
+
+idImageScratch *idRenderSystemLocal::CreateImageForSubview() {
+	// should be called from frontend
+	assert(!com_smp.GetBool() || session->IsFrontend());
+
+	renderCrop_t &rc = renderCrops[currentRenderCrop];
+	int width = rc.width;
+	int height = rc.height;
+
+	int slot = -1;
+	for (int i = 0; i < subviewImages.Num(); i++) {
+		ImageForSubview &si = subviewImages[i];
+
+		if (si.purged) {
+			slot = i;
+			continue;	// GPU texture was freed
+		}
+
+		if (si.lastUsedFrameCount == frameCount) {
+			continue;	// already occupied for this frame
+		}
+
+		if (si.width == width && si.height == height) {
+			// can reuse texture (not freed yet)
+			si.lastUsedFrameCount = frameCount;
+			return si.image;
+		}
+	}
+
+	if (slot < 0) {
+		// add new texture to the end
+		ImageForSubview &added = subviewImages.Alloc();
+		slot = subviewImages.IndexOf(&added);
+		added.width = width;
+		added.height = height;
+
+		idStr imageName = va("$subview$%d", slot);
+		added.image = globalImages->ImageScratch(imageName);
+	}
+
+	ImageForSubview &si = subviewImages[slot];
+	si.purged = false;	// will be generated in backend when data is copied into it
+	si.lastUsedFrameCount = frameCount;
+	return si.image;
+}
+
 static int RoundDownToPowerOfTwo( int v ) {
 	int	i;
 
Index: renderer/tr_local.h
===================================================================
--- renderer/tr_local.h	(revision 10623)
+++ renderer/tr_local.h	(working copy)
@@ -153,6 +153,7 @@
 
 	const struct viewEntity_s *space;
 	const idMaterial		*material;			// may be NULL for shadow volumes
+	idImage					*dynamicImageOverride;	// stgatilov: if not NULL, then texture of material should be replaced with this one (for subviews)
 	float					sort;				// material->sort, modified by gui / entity sort offsets
 	const float				*shaderRegisters;	// evaluated and adjusted for referenceShaders
 	/*const*/ struct drawSurf_s	*nextOnLight;	// viewLight chains
@@ -819,6 +820,13 @@
 
 static const int	MAX_RENDER_CROPS = 8;
 
+struct ImageForSubview {
+	bool purged = false;
+	int width = -1, height = -1;
+	idImageScratch *image = nullptr;
+	int lastUsedFrameCount = INT_MIN;
+};
+
 /*
 ** Most renderer globals are defined here.
 ** backend functions should never modify any of these fields,
@@ -877,6 +885,9 @@
 	void					Clear( void );
 	void					RenderViewToViewport( const renderView_t &renderView, idScreenRect &viewport );
 
+	idImageScratch *		CreateImageForSubview();
+	void					FreeOldSubviewImages();
+
 public:
 	// renderer globals
 	bool					takingScreenshot;
@@ -931,6 +942,8 @@
 
 	unsigned short			gammaTable[256];	// brightness / gamma modify this
 	idParallelJobList*		frontEndJobList;
+
+	idList<ImageForSubview> subviewImages;
 };
 
 extern backEndState_t		backEnd;
